Save selected tags to the display ad model (#18633)

* feat: Add a pack file that pulls in the MultiSelect Component

* feat: move the tags to its own component

* save tags

* refactor: create a getCSRFToken function in the packs files so that it can be used in the admin

* feat: import the new module in request.js

* feat: remove unnecessary id

* feat: first pass of csrf token test

* chore: update the test

* fix: csrf token

* feat: hide the enw functionality behinda  feature flag

* fix: loading form twice

* refactor: import for csrftoken

* chore: update the description of the function

* feat: use acts_on_taggable to craete a relationship between display_ad and tag

* feat: add a tag field to the display_ad form

* feat: add the selected tags from the multiselect autocomplete component to the input text field that references the tag_list

* feat: add the tag_list to the controller so that we can save it to the db with the display ad parameters

* feat: pull out the tag validation from the article and the display_ads into a concern

* feat: write soem tests for validating the tag on the display_ads model

* feat: add the tag_list as a hidden field

* feat: set the selected tags on edit
This commit is contained in:
Ridhwana 2022-10-27 20:22:29 +02:00 committed by GitHub
parent c23b269410
commit cae4c929ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 89 additions and 10 deletions

View file

@ -58,7 +58,8 @@ module Admin
private
def display_ad_params
params.permit(:organization_id, :body_markdown, :placement_area, :published, :approved, :name, :display_to)
params.permit(:organization_id, :body_markdown, :placement_area, :published, :approved, :name, :display_to,
:tag_list)
end
def authorize_admin

View file

@ -8,7 +8,7 @@ import { TagAutocompleteSelection } from '@crayons/MultiSelectAutocomplete/TagAu
import { MultiSelectAutocomplete } from '@crayons';
/**
* Tags for the article form. Allows users to search and select up to 4 tags.
* Tags for the display ads admin form. Allows users to search and select up to 10 tags.
*
* @param {Function} onInput Callback to sync selections to article form state
* @param {string} defaultValue Comma separated list of any currently selected tags

View file

@ -9,15 +9,29 @@ Document.prototype.ready = new Promise((resolve) => {
return null;
});
function saveTags() {}
function saveTags(selectionString) {
document.getElementsByClassName('js-tags-textfield')[0].value =
selectionString;
}
function loadTagsField() {
let defaultValue = '';
const hiddenTagsField =
document.getElementsByClassName('js-tags-textfield')[0];
if (hiddenTagsField) {
defaultValue = hiddenTagsField.value.replaceAll(' ', ', ');
}
const displayAdsTargetedTags = document.getElementById(
'display-ad-targeted-tags',
);
if (displayAdsTargetedTags) {
render(<Tags onInput={saveTags} />, displayAdsTargetedTags);
render(
<Tags onInput={saveTags} defaultValue={defaultValue} />,
displayAdsTargetedTags,
);
}
}

View file

@ -2,6 +2,7 @@ class Article < ApplicationRecord
include CloudinaryHelper
include ActionView::Helpers
include Reactable
include TagListValidateable
include UserSubscriptionSourceable
include PgSearch::Model
@ -768,12 +769,7 @@ class Article < ApplicationRecord
# check there are not too many tags
return errors.add(:tag_list, I18n.t("models.article.too_many_tags")) if tag_list.size > MAX_TAG_LIST_SIZE
# check tags names aren't too long and don't contain non alphabet characters
tag_list.each do |tag|
new_tag = Tag.new(name: tag)
new_tag.validate_name
new_tag.errors.messages[:name].each { |message| errors.add(:tag, "\"#{tag}\" #{message}") }
end
validate_tag_name(tag_list)
end
def remove_tag_adjustments_from_tag_list

View file

@ -0,0 +1,11 @@
module TagListValidateable
extend ActiveSupport::Concern
# we check tags names aren't too long and don't contain non alphabet characters
def validate_tag_name(tag_list)
tag_list.each do |tag|
new_tag = Tag.new(name: tag)
new_tag.validate_name
new_tag.errors.messages[:name].each { |message| errors.add(:tag, "\"#{tag}\" #{message}") }
end
end
end

View file

@ -1,4 +1,6 @@
class DisplayAd < ApplicationRecord
include TagListValidateable
acts_as_taggable_on :tags
resourcify
ALLOWED_PLACEMENT_AREAS = %w[sidebar_left sidebar_left_2 sidebar_right post_comments].freeze
@ -7,6 +9,7 @@ class DisplayAd < ApplicationRecord
"Sidebar Right",
"Below the comment section"].freeze
MAX_TAG_LIST_SIZE = 10
POST_WIDTH = 775
SIDEBAR_WIDTH = 350
@ -18,6 +21,7 @@ class DisplayAd < ApplicationRecord
validates :placement_area, presence: true,
inclusion: { in: ALLOWED_PLACEMENT_AREAS }
validates :body_markdown, presence: true
validate :validate_tag
before_save :process_markdown
after_save :generate_display_ad_name
@ -50,6 +54,13 @@ class DisplayAd < ApplicationRecord
ALLOWED_PLACEMENT_AREAS_HUMAN_READABLE[ALLOWED_PLACEMENT_AREAS.find_index(placement_area)]
end
def validate_tag
# check there are not too many tags
return errors.add(:tag_list, I18n.t("models.article.too_many_tags")) if tag_list.size > MAX_TAG_LIST_SIZE
validate_tag_name(tag_list)
end
private
def generate_display_ad_name

View file

@ -37,6 +37,7 @@ class Tag < ActsAsTaggableOn::Tag
belongs_to :badge, optional: true
has_many :articles, through: :taggings, source: :taggable, source_type: "Article"
has_many :display_ads, through: :taggings, source: :taggable, source_type: "DisplayAd"
mount_uploader :profile_image, ProfileImageUploader
mount_uploader :social_image, ProfileImageUploader

View file

@ -24,6 +24,11 @@
<div class="crayons-field">
<div id="display-ad-targeted-tags"></div>
</div>
<div class="crayons-field hidden">
<%= label_tag :tag_list, "tag_list:", class: "crayons-field__label" %>
<%= text_field_tag :tag_list, @display_ad.tag_list, class: "crayons-textfield js-tags-textfield" %>
</div>
<% end %>
<div class="crayons-field">

View file

@ -137,4 +137,44 @@ RSpec.describe DisplayAd, type: :model do
expect(described_class.search_ads("foo")).to eq([])
end
end
describe ".validate_tag" do
it "rejects more than 10 tags" do
eleven_tags = "one, two, three, four, five, six, seven, eight, nine, ten, eleven"
expect(build(:display_ad,
name: "This is an Ad",
body_markdown: "Ad Body",
placement_area: "post_comments",
tag_list: eleven_tags).valid?).to be(false)
end
it "rejects tags with length > 30" do
tags = "'testing tag length with more than 30 chars', tag"
expect(build(:display_ad,
name: "This is an Ad",
body_markdown: "Ad Body",
placement_area: "post_comments",
tag_list: tags).valid?).to be(false)
end
it "rejects tag with non-alphanumerics" do
expect do
build(:display_ad,
name: "This is an Ad",
body_markdown: "Ad Body",
placement_area: "post_comments",
tag_list: "c++").validate!
end.to raise_error(ActiveRecord::RecordInvalid)
end
it "always downcase tags" do
tags = "UPPERCASE, CAPITALIZE"
display_ad = create(:display_ad,
name: "This is an Ad",
body_markdown: "Ad Body",
placement_area: "post_comments",
tag_list: tags)
expect(display_ad.tag_list).to eq(tags.downcase.split(", "))
end
end
end