CTA Embed (#19440)
* feat: first pass at CTA Tag * fix: strip_tags before using the link * default hover * feat: add the type of cta * feat: add own ltag by copying the utility class * feat: rename property to style * feat: add style and width * feat: add xit test * feat: add spec for unordered options * chore: some css * feat: update the cta spec to remove style and width options * feat: move billboard related code out of widgets * fix: mobile display ad below the comments section * feat: cta_tag strip tags input * feat: update the editor docs * feat: add help text to teh liquid guide and the editor guide * feat: fix the css styles of the CTA * cgore: alphabetical order * chore: move everything on one line --------- Co-authored-by: Mac Siri <mac@forem.com>
This commit is contained in:
parent
a1ca675e6f
commit
d92e9d1af4
14 changed files with 217 additions and 27 deletions
|
|
@ -112,6 +112,16 @@ body {
|
|||
// when/if we "redesign" liquid tags.
|
||||
[class*='ltag'] a {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: var(--color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
a[class*='ltag'] {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: var(--color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
|
|
|
|||
50
app/assets/stylesheets/billboards.scss
Normal file
50
app/assets/stylesheets/billboards.scss
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
@import 'config/import';
|
||||
@import 'variables';
|
||||
@import '_mixins';
|
||||
|
||||
.crayons-layout__comments-display-ad {
|
||||
.crayons-sponsorship {
|
||||
.c-indicator.crayons-sponsorship__indicator {
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
padding-left: var(--content-padding-x);
|
||||
padding-bottom: var(--su-6);
|
||||
|
||||
@media (min-width: $breakpoint-s) {
|
||||
.text-styles {
|
||||
padding-right: var(--su-8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sponsorship-dropdown {
|
||||
hr {
|
||||
border: 0.5px solid var(--divider);
|
||||
margin: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.billboard .ltag_cta {
|
||||
padding-top: var(--su-2);
|
||||
padding-bottom: var(--su-2);
|
||||
|
||||
@media (max-width: $breakpoint-s) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
// refers to the sidebar billboards where we use cards
|
||||
.crayons-card.billboard .ltag_cta {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// these are inline CTA's
|
||||
//refers to the below the comments billboard on the article page
|
||||
//refers to the feed/stories where we use story
|
||||
.crayons-layout__comments-display-ad .crayons-card.billboard .ltag_cta,
|
||||
.crayons-story.billboard .ltag_cta {
|
||||
padding-right: var(--su-7);
|
||||
padding-left: var(--su-7);
|
||||
}
|
||||
49
app/assets/stylesheets/ltags/CtaTag.scss
Normal file
49
app/assets/stylesheets/ltags/CtaTag.scss
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
.ltag_cta {
|
||||
--border-width: 1px;
|
||||
padding: var(--su-2) var(--su-4);
|
||||
border: var(--border-width) solid;
|
||||
border-color: var(--border);
|
||||
text-decoration: none;
|
||||
border-radius: var(--radius);
|
||||
font: inherit;
|
||||
background-color: var(--bg);
|
||||
color: var(--color);
|
||||
outline: 0;
|
||||
text-align: center;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
overflow-wrap: normal;
|
||||
|
||||
&:hover,
|
||||
.js-focus-visible &.focus-visible:focus {
|
||||
background-color: var(--bg-hover);
|
||||
border-color: var(--border-hover);
|
||||
color: var(--color-hover);
|
||||
z-index: var(--z-elevate);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.js-focus-visible &.focus-visible:focus {
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
}
|
||||
|
||||
.ltag_cta {
|
||||
--bg: var(--cta-bg);
|
||||
--bg-hover: var(--cta-bg-hover);
|
||||
--color: var(--cta-color);
|
||||
--color-hover: var(--cta-color-hover);
|
||||
--border: var(--cta-border);
|
||||
--border-hover: var(--cta-border-hover);
|
||||
|
||||
&--branded {
|
||||
--bg: var(--cta-branded-bg);
|
||||
--bg-hover: var(--cta-branded-bg-hover);
|
||||
--color: var(--cta-branded-color);
|
||||
--color-hover: var(--cta-branded-color-hover);
|
||||
--border: var(--cta-branded-border);
|
||||
--border-hover: var(--cta-branded-border-hover);
|
||||
|
||||
font-weight: var(--fw-medium);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
@import 'CommentTag';
|
||||
@import 'CtaTag';
|
||||
@import 'GistTag';
|
||||
@import 'GithubReadmeTag';
|
||||
@import 'GithubTag';
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
@import 'scaffolds';
|
||||
@import 'articles';
|
||||
@import 'article-show';
|
||||
@import 'billboards';
|
||||
@import 'shared';
|
||||
@import 'dashboard';
|
||||
@import 'settings';
|
||||
|
|
|
|||
|
|
@ -351,24 +351,3 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.crayons-layout__comments-display-ad {
|
||||
.crayons-sponsorship {
|
||||
.c-indicator.crayons-sponsorship__indicator {
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
padding-left: var(--content-padding-x);
|
||||
padding-bottom: var(--su-6);
|
||||
.text-styles {
|
||||
padding-right: var(--su-8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sponsorship-dropdown {
|
||||
hr {
|
||||
border: 0.5px solid var(--divider);
|
||||
margin: 8px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
app/liquid_tags/cta_tag.rb
Normal file
36
app/liquid_tags/cta_tag.rb
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
class CtaTag < Liquid::Block
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
PARTIAL = "liquids/cta".freeze
|
||||
# at some point we may want to pass in options to dictate which type of CTA the user wants to use,
|
||||
# i.e. secondary, primary, branded. This sets the scene for it without actually providing that option now.
|
||||
TYPE_OPTIONS = %w[branded].freeze
|
||||
DESCRIPTION_LENGTH = 128
|
||||
|
||||
def initialize(_tag_name, options, _parse_context)
|
||||
super
|
||||
@link = strip_tags(options.strip)
|
||||
end
|
||||
|
||||
def render(_context)
|
||||
content = Nokogiri::HTML.parse(super)
|
||||
|
||||
ApplicationController.render(
|
||||
partial: PARTIAL,
|
||||
locals: {
|
||||
link: @link,
|
||||
description: sanitized_description(content),
|
||||
type: TYPE_OPTIONS.first
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sanitized_description(content)
|
||||
stripped_description = strip_tags(content.xpath("//html/body").inner_html).delete("\n").strip
|
||||
stripped_description.truncate(DESCRIPTION_LENGTH)
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag("cta", CtaTag)
|
||||
1
app/views/liquids/_cta.html.erb
Normal file
1
app/views/liquids/_cta.html.erb
Normal file
|
|
@ -0,0 +1 @@
|
|||
<a href="<%= link %>" class="ltag_cta ltag_cta--<%= type %>" role="button"><%= description %></a>
|
||||
|
|
@ -45,11 +45,15 @@
|
|||
|
||||
<h3>Supported Non-URL Embeds</h3>
|
||||
|
||||
<h4>Call To Action (CTA)</h4>
|
||||
<p><pre>{% cta link %} description {% endcta %}</pre></p>
|
||||
<p>Provide a <em>link</em> that a user will be redirected to. The <em>description</em> will contain the label/description for the call to action.</p>
|
||||
|
||||
<h4>Details</h4>
|
||||
<p>You can embed a <code>details</code> HTML element by using details, spoiler, or collapsible. The <em>summary</em> will be what the dropdown title displays. The <em>content</em> will be the text hidden behind the dropdown. This is great for when you want to hide text (i.e. answers to questions) behind a user action/intent (i.e. a click).</p>
|
||||
<p><code>{% details summary %} content {% enddetails %}</code></p>
|
||||
<p><code>{% spoiler summary %} content {% endspoiler %}</code></p>
|
||||
<p><code>{% collapsible summary %} content {% endcollapsible %}</code></p>
|
||||
<p><pre>{% details summary %} content {% enddetails %}</pre></p>
|
||||
<p><pre>{% spoiler summary %} content {% endspoiler %}</pre></p>
|
||||
<p><pre>{% collapsible summary %} content {% endcollapsible %}</pre></p>
|
||||
|
||||
<h4>KaTex</h4>
|
||||
<p>Place your mathematical expression within a KaTeX liquid block, as follows:</p>
|
||||
|
|
@ -65,7 +69,7 @@
|
|||
<h4><%= community_name %> User Subscriptions</h4>
|
||||
<p class="fs-s fw-bold">This embed can only be created within posts by Admins.</p>
|
||||
<p>You can add call-to-action text that will show above the subscribe button:</p>
|
||||
<code>{% user_subscription If you'd like to receive future updates, subscribe below! %}</code>
|
||||
<pre>{% user_subscription If you'd like to receive future updates, subscribe below! %}</pre>
|
||||
<p class="fs-s">
|
||||
If a reader is signed out, the button will prompt them to sign in first
|
||||
to subscribe. If the reader is signed in, the button will prompt them
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@
|
|||
|
||||
<h3>Supported Non-URL Embeds</h3>
|
||||
|
||||
<h4>Call To Action (CTA)</h4>
|
||||
<p><pre>{% cta link %} description {% endcta %}</pre></p>
|
||||
<p>Provide a <em>link</em> that a user will be redirected to. The <em>description</em> will contain the label/description for the call to action.</p>
|
||||
|
||||
<h4>Details</h4>
|
||||
<p>You can embed a <code>details</code> HTML element by using details, spoiler, or collapsible. The <em>summary</em> will be what the dropdown title displays. The <em>content</em> will be the text hidden behind the dropdown. This is great for when you want to hide text (i.e. answers to questions) behind a user action/intent (i.e. a click).</p>
|
||||
<p><code>{% details summary %} content {% enddetails %}</code></p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<% @user_approved_liquid_tags = Users::ApprovedLiquidTags.call(@user) %>
|
||||
|
||||
<h4>Call To Action (CTA)</h4>
|
||||
<p><pre>{% cta link %} description {% endcta %}</pre></p>
|
||||
<p>Provide a <em>link</em> that a user will be redirected to. The <em>description</em> will contain the label/description for the call to action.</p>
|
||||
|
||||
<h4>Details</h4>
|
||||
<p>You can embed a <code>details</code> HTML element by using details, spoiler, or collapsible. The <em>summary</em> will be what the dropdown title displays. The <em>content</em> will be the text hidden behind the dropdown. This is great for when you want to hide text (i.e. answers to questions) behind a user action/intent (i.e. a click).</p>
|
||||
<pre>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<% @user_approved_liquid_tags = Users::ApprovedLiquidTags.call(@user) %>
|
||||
|
||||
<h4>Call To Action (CTA)</h4>
|
||||
<p><pre>{% cta link %} description {% endcta %}</pre></p>
|
||||
<p>Provide a <em>link</em> that a user will be redirected to. The <em>description</em> will contain the label/description for the call to action.</p>
|
||||
|
||||
<h4>Details</h4>
|
||||
<p>You can embed a <code>details</code> HTML element by using details, spoiler, or collapsible. The <em>summary</em> will be what the dropdown title displays. The <em>content</em> will be the text hidden behind the dropdown. This is great for when you want to hide text (i.e. answers to questions) behind a user action/intent (i.e. a click).</p>
|
||||
<pre>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<% if display_ad.placement_area.start_with?("feed_") %>
|
||||
<div class="crayons-story crayons-story__display-ad"
|
||||
<div class="crayons-story crayons-story__display-ad billboard"
|
||||
data-display-unit data-id="<%= display_ad.id %>"
|
||||
data-category-click="<%= DisplayAdEvent::CATEGORY_CLICK %>"
|
||||
data-category-impression="<%= DisplayAdEvent::CATEGORY_IMPRESSION %>"
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="crayons-card crayons-card--secondary crayons-sponsorship"
|
||||
<div class="crayons-card crayons-card--secondary crayons-sponsorship billboard"
|
||||
data-display-unit data-id="<%= display_ad.id %>"
|
||||
data-category-click="<%= DisplayAdEvent::CATEGORY_CLICK %>"
|
||||
data-category-impression="<%= DisplayAdEvent::CATEGORY_IMPRESSION %>"
|
||||
|
|
|
|||
47
spec/liquid_tags/cta_tag_spec.rb
Normal file
47
spec/liquid_tags/cta_tag_spec.rb
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe CtaTag, type: :liquid_tag do
|
||||
describe "#render" do
|
||||
let(:link) { "https://dev.to/" }
|
||||
let(:description) { "DEV Community" }
|
||||
|
||||
def generate_details_liquid(options, description)
|
||||
Liquid::Template.register_tag("cta", described_class)
|
||||
Liquid::Template.parse("{% cta #{options} %} #{description} {% endcta %}")
|
||||
end
|
||||
|
||||
it "contains the correct static attributes" do
|
||||
rendered = generate_details_liquid(link, description).render
|
||||
|
||||
expect(rendered).to include("class=\"ltag_cta ltag_cta--branded")
|
||||
expect(rendered).to include("role=\"button")
|
||||
end
|
||||
|
||||
it "generates the correct href attribute" do
|
||||
rendered = generate_details_liquid(link, description).render
|
||||
expect(rendered).to include("href=\"#{link}")
|
||||
end
|
||||
|
||||
it "contains the correct description" do
|
||||
rendered = generate_details_liquid(link, description).render
|
||||
expect(rendered).to include(description)
|
||||
end
|
||||
|
||||
it "limits the description to 128 characters" do
|
||||
long_description = "We do not allow for more than hundred and twenty eight characters in the description of " \
|
||||
"the CTA. This is the same as what we allow for article titles."
|
||||
rendered = generate_details_liquid(link, long_description).render
|
||||
expect(rendered).to include("We do not allow for more than hundred and twenty eight characters in the " \
|
||||
"description of the CTA. This is the same as what we ...")
|
||||
expect(rendered).not_to include("article titles.")
|
||||
end
|
||||
|
||||
it "strips all tags from the description" do
|
||||
rendered = generate_details_liquid(link, "<div class='crayons'>DEV Community</div>").render
|
||||
expect(rendered).to include("DEV Community")
|
||||
expect(rendered).not_to include("class='crayons'")
|
||||
expect(rendered).not_to include("<div")
|
||||
expect(rendered).not_to include("</div>")
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue