@@ -112,10 +124,37 @@ describe('getBillboard', () => {
await getBillboard();
- // Check if the fetch function was called with the modified URL including the parameters
- expect(global.fetch).toHaveBeenCalledWith(
- '/billboards/post_sidebar?bb_test_placement_area=post_sidebar&bb_test_id=1',
+ expect(global.fetch).toHaveBeenCalledWith('/billboards/post_sidebar?bb_test_placement_area=post_sidebar&bb_test_id=1');
+ });
+
+ test('should display none if dismissal SKU matches', async () => {
+ window.localStorage.setItem('dismissal_skus_triggered', JSON.stringify(['sku123']));
+
+ global.fetch = jest.fn(() =>
+ Promise.resolve({
+ text: () => Promise.resolve('
'),
+ }),
);
+
+ await getBillboard();
+
+ const billboardContent = document.querySelector('.js-billboard-container div');
+ expect(billboardContent.closest('.js-billboard-container').style.display).toBe('none');
+ });
+
+ test('should display billboard content if there is no matching dismissal SKU', async () => {
+ window.localStorage.setItem('dismissal_skus_triggered', JSON.stringify(['sku999']));
+
+ global.fetch = jest.fn(() =>
+ Promise.resolve({
+ text: () => Promise.resolve('
'),
+ }),
+ );
+
+ await getBillboard();
+
+ const billboardContent = document.querySelector('.js-billboard-container div');
+ expect(billboardContent.closest('.js-billboard-container').style.display).toBe(''); // Not marked as display none
});
});
@@ -141,10 +180,7 @@ describe('executeBBScripts', () => {
test('should skip null or undefined script elements', () => {
container.innerHTML = '';
- // Simulating an inconsistency in the returned HTMLCollection
- const spiedGetElementsByTagName = jest
- .spyOn(container, 'getElementsByTagName')
- .mockReturnValue([null, undefined]);
+ const spiedGetElementsByTagName = jest.spyOn(container, 'getElementsByTagName').mockReturnValue([null, undefined]);
executeBBScripts(container);
@@ -152,8 +188,7 @@ describe('executeBBScripts', () => {
});
test('should copy attributes of original script element', () => {
- container.innerHTML =
- '';
+ container.innerHTML = '';
executeBBScripts(container);
@@ -171,8 +206,7 @@ describe('executeBBScripts', () => {
});
test('should insert the new script element at the same position as the original', () => {
- container.innerHTML =
- '
';
executeBBScripts(container);
diff --git a/app/javascript/packs/billboard.js b/app/javascript/packs/billboard.js
index e3cc9282f..a9972954f 100644
--- a/app/javascript/packs/billboard.js
+++ b/app/javascript/packs/billboard.js
@@ -23,7 +23,7 @@ async function generateBillboard(element) {
}
if (cookieStatus === 'allowed') {
- asyncUrl += `${asyncUrl.includes('?') ? '&' : '?' }cookies_allowed=true`;
+ asyncUrl += `${asyncUrl.includes('?') ? '&' : '?'}cookies_allowed=true`;
}
if (asyncUrl) {
@@ -39,6 +39,15 @@ async function generateBillboard(element) {
this.style.display = 'none';
};
});
+ const dismissalSku =
+ element.querySelector('.js-billboard')?.dataset.dismissalSku;
+ if (localStorage && dismissalSku && dismissalSku.length > 0) {
+ const skuArray =
+ JSON.parse(localStorage.getItem('dismissal_skus_triggered')) || [];
+ if (skuArray.includes(dismissalSku)) {
+ element.style.display = 'none';
+ }
+ }
executeBBScripts(element);
implementSpecialBehavior(element);
setupBillboardInteractivity();
diff --git a/app/javascript/utilities/__tests__/billboardInteractivity.test.js b/app/javascript/utilities/__tests__/billboardInteractivity.test.js
index 0878f635c..aeb78e4c6 100644
--- a/app/javascript/utilities/__tests__/billboardInteractivity.test.js
+++ b/app/javascript/utilities/__tests__/billboardInteractivity.test.js
@@ -6,7 +6,7 @@ describe('billboard close functionality', () => {
// Setup a simple DOM structure that includes only the elements needed for the close functionality
document.body.innerHTML = `
+
`;
@@ -35,4 +35,16 @@ describe('billboard close functionality', () => {
const billboard = document.querySelector('.js-billboard');
expect(billboard.style.display).toBe('none');
});
+
+ it('adds dismissal sku to local storage when dismissing a billboard', () => {
+ setupBillboardInteractivity();
+
+ // Simulate clicking the close button
+ const closeButton = document.querySelector('#sponsorship-close-trigger-1');
+ closeButton.click();
+
+ // Assert the dismissal sku is added to local storage
+ const dismissalSkus = JSON.parse(localStorage.getItem('dismissal_skus_triggered'));
+ expect(dismissalSkus).toEqual(['WHATUP']);
+ });
});
\ No newline at end of file
diff --git a/app/javascript/utilities/billboardInteractivity.jsx b/app/javascript/utilities/billboardInteractivity.jsx
index 2f56e1252..b82449560 100644
--- a/app/javascript/utilities/billboardInteractivity.jsx
+++ b/app/javascript/utilities/billboardInteractivity.jsx
@@ -29,11 +29,11 @@ export function setupBillboardInteractivity() {
if (sponsorshipCloseButtons.length) {
sponsorshipCloseButtons.forEach((sponsorshipCloseButton) => {
sponsorshipCloseButton.addEventListener('click', () => {
- sponsorshipCloseButton.closest('.js-billboard').style.display = 'none';
+ dismissBillboard(sponsorshipCloseButton);
});
document.addEventListener('click', (event) => {
if (!event.target.closest('.js-billboard')) {
- sponsorshipCloseButton.closest('.js-billboard').style.display = 'none';
+ dismissBillboard(sponsorshipCloseButton);
}
});
});
@@ -51,3 +51,15 @@ function amendBillboardStyle(sponsorshipDropdownButton) {
'revert';
}
}
+
+function dismissBillboard(sponsorshipCloseButton) {
+ const sku = sponsorshipCloseButton.closest('.js-billboard').dataset.dismissalSku;
+ sponsorshipCloseButton.closest('.js-billboard').style.display = 'none';
+ if (localStorage && sku && sku.length > 0) {
+ const skuArray = JSON.parse(localStorage.getItem('dismissal_skus_triggered')) || [];
+ if (!skuArray.includes(sku)) {
+ skuArray.push(sku);
+ localStorage.setItem('dismissal_skus_triggered', JSON.stringify(skuArray));
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/views/shared/_billboard.html.erb b/app/views/shared/_billboard.html.erb
index 892ec5c9b..ac113bc72 100644
--- a/app/views/shared/_billboard.html.erb
+++ b/app/views/shared/_billboard.html.erb
@@ -49,6 +49,7 @@
data-category-click="<%= BillboardEvent::CATEGORY_CLICK %>"
data-category-impression="<%= BillboardEvent::CATEGORY_IMPRESSION %>"
data-context-type="<%= data_context_type %>"
+ data-dismissal-sku="<%= billboard.dismissal_sku %>"
data-special="<%= billboard.special_behavior %>"
data-article-id="<%= @article&.id %>"
data-type-of="<%= billboard.type_of %>">
diff --git a/db/migrate/20240301160047_add_dismissal_sku_to_billboards.rb b/db/migrate/20240301160047_add_dismissal_sku_to_billboards.rb
new file mode 100644
index 000000000..a960c5c49
--- /dev/null
+++ b/db/migrate/20240301160047_add_dismissal_sku_to_billboards.rb
@@ -0,0 +1,5 @@
+class AddDismissalSkuToBillboards < ActiveRecord::Migration[7.0]
+ def change
+ add_column :display_ads, :dismissal_sku, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b4742e0e4..5372b180a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2024_02_27_181740) do
+ActiveRecord::Schema[7.0].define(version: 2024_03_01_160047) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
enable_extension "ltree"
@@ -479,6 +479,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_02_27_181740) do
t.datetime "created_at", precision: nil, null: false
t.integer "creator_id"
t.string "custom_display_label"
+ t.string "dismissal_sku"
t.integer "display_to", default: 0, null: false
t.integer "exclude_article_ids", default: [], array: true
t.integer "impressions_count", default: 0
diff --git a/spec/requests/api/v1/billboards_spec.rb b/spec/requests/api/v1/billboards_spec.rb
index 3d05aa5fb..023f093c4 100644
--- a/spec/requests/api/v1/billboards_spec.rb
+++ b/spec/requests/api/v1/billboards_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe "Api::V1::Billboards" do
"impressions_count", "name", "organization_id",
"placement_area", "processed_html", "published",
"success_rate", "tag_list", "type_of", "updated_at",
- "creator_id", "exclude_article_ids",
+ "creator_id", "exclude_article_ids", "dismissal_sku",
"audience_segment_type", "audience_segment_id",
"custom_display_label", "template", "render_mode", "preferred_article_ids",
"priority", "weight", "target_geolocations", "requires_cookies", "special_behavior")
@@ -71,7 +71,7 @@ RSpec.describe "Api::V1::Billboards" do
"impressions_count", "name", "organization_id",
"placement_area", "processed_html", "published",
"success_rate", "tag_list", "type_of", "updated_at",
- "creator_id", "exclude_article_ids",
+ "creator_id", "exclude_article_ids", "dismissal_sku",
"audience_segment_type", "audience_segment_id",
"custom_display_label", "template", "render_mode", "preferred_article_ids",
"priority", "weight", "target_geolocations", "requires_cookies", "special_behavior")
@@ -135,7 +135,7 @@ RSpec.describe "Api::V1::Billboards" do
contain_exactly("approved", "body_markdown", "cached_tag_list",
"clicks_count", "created_at", "display_to", "id",
"impressions_count", "name", "organization_id",
- "placement_area", "processed_html", "published",
+ "placement_area", "processed_html", "published", "dismissal_sku",
"success_rate", "tag_list", "type_of", "updated_at",
"creator_id", "exclude_article_ids", "requires_cookies",
"audience_segment_type", "audience_segment_id", "special_behavior",