Showing and hiding tags in the Following and Hidden Tags Pages (#19954)

* feat: remove updated weights

* feat: click on following on the following tags page

* feat: add and listen for the following button click

* fix: dropdowns

* feat: handle hide button click

* feat: update the nav link for following tags

* feat: handel the 'unhide button' on the hidden tags page

* feat: add the styling for the buttons

* feat: add localization

* feat: remove the brand from the card

* feat: add some styling to the page

* feat: init scrolling

* chore: update the name of the file

* feat: abstract out the comment fetch code

* fix: close the attribute

* feta: make some adjustments to where the tag adn the follow show up

* rename class from plural to singular

* feat: updae the comment

* feat: add a mutation observer to initialize the dropdown

* test: follow_craete

* chore: remove irrelevant test

* feat: update the cypress seeds

* feat: update the cypress seeds

* spec: write soem initial tests for the following tags page

* spec: update the unfollow cypress spec

* spec: update the hide cypress spec

* spec: update the posts published spec

* note to add test for pagination

* spec: hidden tags page

* feat: remove aria pressed attributes

* fix: ordering by explicit points gets the page params confused and returns the incorrect and not all results on pagination

* fix: use explicit points

* feat: remove the message at the top of the following and hidden tags page

* chore: remove comment and rather add it to the PR itself

* refactor: do all the actions on success

* feat: refactor the dashboard tag file

* refactor: only show the tagId and followId dataset attributes on the dashboard__tag__container

* feat: disconnect the observer

* chore: add some documentation

* feat: add fr localization

* chore: empty line

* fix: update the dashboardTags page to first declare the observer"
"
"

* fix: update the rails test

* Empty commit

* fix: because I added more tags, the dropdown was longre and covering the body of the editor hence it could not find the text, so I've escaped after selecting my tags
This commit is contained in:
Ridhwana 2023-08-25 14:49:39 +02:00 committed by GitHub
parent 5ef0f43636
commit 5266dd1afb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 486 additions and 113 deletions

View file

@ -140,20 +140,62 @@ function buildFollowsHTML(follows) {
* @returns an HTML block for a tag follow.
*/
function buildTagsHTML(tag) {
var antifollow = '';
if (tag.points < 0) {
antifollow =
'<span class="c-indicator c-indicator--danger" title="This tag has negative follow weight">Anti-follow</span>';
let followingButtonContainer = '';
let unhideButtonContainer = '';
if (tag.explicit_points < 0) {
unhideButtonContainer = `<div>
<button class="crayons-btn crayons-btn--danger unhide-button">Unhide</button>
</div>`;
}
return `<div class="crayons-card branded-2 p-4 m:p-6 m:pt-4 flex flex-col single-article break-word content-center" style="border-top-color: ${tag.color};" id="follows-${tag.id}">
<h3 class="s:mb-1 -ml-1 p-0 fw-medium">
if (tag.explicit_points >= 0) {
followingButtonContainer = `<div class="flex justify-between">
<div>
<button class="c-btn c-btn--secondary follow-button" aria-label="Following tag:${tag.name}">Following</button>
</div>
<div class="dropdown-trigger-container relative">
<button type="button" aria-label="Unhide tag: ${tag.name}" class="c-btn c-btn--icon-alone dropdown-trigger" id="options-dropdown-trigger-${tag.tag_id}" aria-haspopup="true" aria-expanded="false" aria-controls="options-dropdown">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="crayons-icon" role="img" aria-labelledby="arimcllskwkmbfpxyu52b8vzin20raon"><title id="arimcllskwkmbfpxyu52b8vzin20raon">Options</title><path fill-rule="evenodd" clip-rule="evenodd" d="M7 12a2 2 0 11-4 0 2 2 0 014 0zm7 0a2 2 0 11-4 0 2 2 0 014 0zm5 2a2 2 0 100-4 2 2 0 000 4z"></path></svg>
</button>
<div class="crayons-dropdown right-0 left-0 s:left-auto" id="options-dropdown-${tag.tag_id}">
<ul class="p-0">
<li>
<button type="button" class="hide-button c-btn w-100 align-left flex items-center" aria-label="Hide tag: ${tag.name}">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" class="crayons-icon" role="img" aria-labelledby="avelns726vzvmu4rpqxq8olxhll9obh"><title id="avelns726vzvmu4rpqxq8olxhll9obh">Hide tag</title>
<path d="M17.882 19.297A10.95 10.95 0 0 1 12 21c-5.392 0-9.878-3.88-10.82-9a10.982 10.982 0 0 1 3.34-6.066L1.393 2.808l1.415-1.415 19.799 19.8-1.415 1.414-3.31-3.31zM5.935 7.35A8.965 8.965 0 0 0 3.223 12a9.006 9.006 0 0 0 13.2 5.838l-2.027-2.028A4.5 4.5 0 0 1 8.19 9.604L5.935 7.35zm6.979 6.978-3.242-3.242a2.5 2.5 0 0 0 3.24 3.241l.002.001zm7.893 2.264-1.431-1.43a8.936 8.936 0 0 0 1.4-3.162A9.006 9.006 0 0 0 9.553 5.338L7.974 3.76A10.99 10.99 0 0 1 12 3c5.392 0 9.878 3.88 10.819 9a10.949 10.949 0 0 1-2.012 4.592zm-9.084-9.084a4.5 4.5 0 0 1 4.769 4.77l-4.77-4.77z"></path>
</svg>
<span class="ml-2">Hide tag</span>
</button>
</li>
</ul>
</div>
</div>
</div>`;
}
let short_summary = '';
if (tag.short_summary) {
short_summary = `<p class="mb-6 fs-s color-base-70 truncate-at-3">${tag.short_summary}</p>`;
}
// TODO: remove the data-follow-id and the data-tag-id in the child components
return `
<div class="dashboard__tag__container crayons-card p-3 pt-2 m:p-5 m:pt-4 relative flex flex-col single-article break-word content-center" id="follows-${
tag.id
}" data-follow-id="${tag.id}" data-tag-id="${tag.tag_id}">
<div class="mb-1 flex items-center justify-between">
<h4 class="-ml-2">
<a href="/t/${tag.name}" class="crayons-tag crayons-tag--l">
<span class="crayons-tag__prefix">#</span>${tag.name}
</a>
</h3>
<input name="follows[][id]" id="follow_id_${tag.name}" type="hidden" form="follows_update_form" value="${tag.id}">
<input step="any" class="crayons-textfield flex-1 fs-s" required="required" type="number" form="follows_update_form" value="${tag.explicit_points}" name="follows[][explicit_points]" id="follow_points_${tag.name}" aria-label="${tag.name} tag weight">
</h4>
<div class="fs-xs color-base-60">${tag.taggings_count.toLocaleString()} posts</div>
</div>
${short_summary}
${unhideButtonContainer}
${followingButtonContainer}
</div>`;
}

View file

@ -400,3 +400,14 @@ $column-flex: 1;
font-weight: bold;
}
}
// dashboard pages
.dashboard__tag__container {
height: 195px;
justify-content: space-between;
.crayons-tag {
padding-top: 0;
padding-bottom: 0;
}
}

View file

@ -19,7 +19,7 @@ class FollowingsController < ApplicationController
else
relation.where(explicit_points: (0...))
end
relation = relation.order(explicit_points: :desc)
relation = relation.order(created_at: :desc)
@followed_tags = load_follows_and_paginate(relation)
end

View file

@ -103,6 +103,7 @@ class FollowsController < ApplicationController
def follow(followable, need_notification: false)
user_follow = current_user.follow(followable)
user_follow.update!(explicit_points: params[:explicit_points]) if params[:explicit_points].present?
Notification.send_new_follower_notification(user_follow) if need_notification
I18n.t("follows_controller.followed")
rescue ActiveRecord::RecordInvalid

View file

@ -0,0 +1,178 @@
import { initializeDropdown } from '@utilities/dropdownUtils';
listenForButtonClicks();
/**
* Adds an event listener to the inner page content, to handle any and all follow button clicks with a single handler
*/
function listenForButtonClicks() {
document
.getElementById('following-wrapper')
.addEventListener('click', handleClick);
}
/**
* Checks a click event's target to see which button was clicked and calls the relevant handlers
*
* @param {HTMLElement} target The target of the click event
*/
function handleClick({ target }) {
const tagContainer = target.closest('.dashboard__tag__container');
if (target.classList.contains('follow-button')) {
handleFollowingButtonClick(tagContainer);
}
if (target.classList.contains('hide-button')) {
handleHideButtonClick(tagContainer);
}
if (target.classList.contains('unhide-button')) {
handleUnhideButtonClick(tagContainer);
}
}
function fetchFollows(body) {
const tokenMeta = document.querySelector("meta[name='csrf-token']");
const csrfToken = tokenMeta && tokenMeta.getAttribute('content');
return window.fetch('/follows', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
credentials: 'same-origin',
});
}
function handleFollowingButtonClick(tagContainer) {
const { tagId, followId } = tagContainer.dataset;
const data = {
followable_type: 'Tag',
followable_id: tagId,
verb: 'unfollow',
};
fetchFollows(data)
.then(() => {
removeElementFromPage(followId);
updateNavigationItemCount();
})
.catch((error) => console.error(error));
}
function handleHideButtonClick(tagContainer) {
const { tagId, followId } = tagContainer.dataset;
const data = {
followable_type: 'Tag',
followable_id: tagId,
verb: 'follow',
explicit_points: -1,
};
fetchFollows(data)
.then(() => {
removeElementFromPage(followId);
// update the current navigation item count
updateNavigationItemCount();
// update the hidden tags navigation item
const hiddenTagsNavigationItem = document.querySelector(
'.js-hidden-tags-link .c-indicator',
);
updateNavigationItemCount(hiddenTagsNavigationItem, 1);
})
.catch((error) => console.error(error));
}
function handleUnhideButtonClick(tagContainer) {
const { tagId, followId } = tagContainer.dataset;
const data = {
followable_type: 'Tag',
followable_id: tagId,
verb: 'follow',
explicit_points: 1,
};
fetchFollows(data)
.then(() => {
removeElementFromPage(followId);
// update the current navigation item count
updateNavigationItemCount();
// update the following tags navigation item
const followingTagsNavigationItem = document.querySelector(
'.js-following-tags-link .c-indicator',
);
updateNavigationItemCount(followingTagsNavigationItem, 1);
})
.catch((error) => console.error(error));
}
function removeElementFromPage(followId) {
document.getElementById(`follows-${followId}`).remove();
}
function updateNavigationItemCount(
navItem = document.querySelector('.crayons-link--current .c-indicator'),
adjustment = -1,
) {
const currentFollowingTagsCount = parseInt(navItem.innerHTML, 10);
navItem.textContent = currentFollowingTagsCount + adjustment;
}
/**
* Initializes the dropdown within each card
*/
const allButtons = document.querySelectorAll('.follow-button');
allButtons.forEach((button) => {
const { tagId } = button.closest('.dashboard__tag__container').dataset;
initializeDropdown({
triggerElementId: `options-dropdown-trigger-${tagId}`,
dropdownContentId: `options-dropdown-${tagId}`,
});
});
/**
* When there is a change to the DOMTree, we find the added node and initializes the dropdown
*/
const observer = new MutationObserver((mutationsList) => {
mutationsList.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
// to remove options like #text '\n '
if (node.hasChildNodes()) {
const { tagId } = node.closest('.dashboard__tag__container').dataset;
initializeDropdown({
triggerElementId: `options-dropdown-trigger-${tagId}`,
dropdownContentId: `options-dropdown-${tagId}`,
});
}
});
}
});
});
/**
* Observes when there additions to the DOM(like when we paginate) within the wrapper
*/
document.querySelectorAll('#following-wrapper').forEach((tagContainer) => {
observer.observe(tagContainer, {
childList: true,
subtree: true,
});
});
InstantClick.on('change', () => {
observer.disconnect();
});
window.addEventListener('beforeunload', () => {
observer.disconnect();
});

View file

@ -1,36 +0,0 @@
document
.getElementById('follows_update_form')
.addEventListener('submit', checkChanged);
document.addEventListener('change', (event) => {
if (event.target && event.target.name == 'follows[][explicit_points]') {
addChanged(event.target);
}
});
function addChanged(element) {
element.setAttribute('changed', true);
}
function checkChanged(event) {
if (document.querySelector('input[changed]')) {
disableAllUnchanged();
} else {
event.preventDefault();
}
}
function disableAllUnchanged() {
document.querySelectorAll('div[id^="follows"]').forEach(disableUnchanged);
}
function disableUnchanged(item) {
const inputs = item.getElementsByTagName('input');
const id = inputs[0];
const point = inputs[1];
if (!point.hasAttribute('changed')) {
point.setAttribute('disabled', true);
id.setAttribute('disabled', true);
}
}

View file

@ -30,7 +30,7 @@
<% end %>
<li>
<a class="crayons-link crayons-link--block <%= "crayons-link--current" if params[:action] == "following_tags" %>"
<a class="crayons-link crayons-link--block js-following-tags-link <%= "crayons-link--current" if params[:action] == "following_tags" %>"
href="<%= dashboard_following_tags_path %>"
<%= params[:action] == "following_tags" ? ' aria-current="page"'.html_safe : "" %>>
<%= t("views.dashboard.actions.following_tags") %>
@ -83,7 +83,7 @@
<%- end %>
<li>
<a class="crayons-link crayons-link--block <%= "crayons-link--current" if params[:action] == "hidden_tags" %>"
<a class="crayons-link crayons-link--block js-hidden-tags-link <%= "crayons-link--current" if params[:action] == "hidden_tags" %>"
href="<%= dashboard_hidden_tags_path %>"
<%= params[:action] == "hidden_tags" ? ' aria-current="page"'.html_safe : "" %>>
<%= t("views.dashboard.actions.hidden_tags") %>

View file

@ -1,28 +1,51 @@
<%= javascript_packs_with_chunks_tag "dashboardTagsDisableUnchangedButtons", defer: true %>
<%= javascript_packs_with_chunks_tag "dashboardTags", defer: true %>
<div id="following-wrapper" class="grid gap-3 m:gap-4 s:grid-cols-2 l:grid-cols-3 px-3 m:px-0">
<% tags.each do |follow| %>
<% tag = follow.followable %>
<% if tag %>
<div class="crayons-card branded-2 p-4 m:p-6 m:pt-4 flex flex-col single-article break-word content-center" style="border-top-color: <%= tag.bg_color_hex %>;" id="follows-<%= follow.id %>">
<h3 class="s:mb-1 -ml-1 p-0 fw-medium">
<%= render_tag_link(tag.name) %>
</h3>
<p class="grid-cell__summary truncate-at-2 mb-4 fs-m h-50">
<%= strip_tags(tag.short_summary) %>
</p>
<%= fields(follow) do |f| %>
<%= f.hidden_field(:id, name: "follows[][id]", form: "follows_update_form", id: "follow_id_#{follow.followable}") %>
<%= f.number_field(:explicit_points, step: :any, required: true, class: "crayons-textfield flex-1 fs-s", name: "follows[][explicit_points]", form: "follows_update_form", "aria-label": t("views.dashboard.tags.number.aria_label", name: follow.followable),
id: "explicit_points_#{follow.followable}") %>
<div class="dashboard__tag__container crayons-card p-3 pt-2 m:p-5 m:pt-4 relative flex flex-col single-article break-word content-center" id="follows-<%= follow.id %>" data-follow-id="<%= follow.id %>" data-tag-id="<%= tag.id %>">
<div class="mb-1 flex items-center justify-between">
<h4 class="-ml-2">
<%= render_tag_link(tag.name) %>
</h4>
<div class="fs-xs color-base-60"><%= t("views.tags.published", count: number_with_delimiter(tag.taggings_count, delimiter: ",")) %></div>
</div>
<% if tag.short_summary.present? %>
<p class="mb-6 fs-s color-base-70 truncate-at-3"><%= sanitize tag.short_summary %></p>
<% end %>
<div class="dashboard__tag__buttons-container">
<%# we could use positive explicit points %>
<% if page === "following" %>
<div class="flex justify-between">
<div>
<button class="c-btn c-btn--secondary follow-button" aria-label="Following tag: <%= tag.name %>"><%= t("views.dashboard.tags.following_button") %></button>
</div>
<div class="dropdown-trigger-container relative">
<button type="button" class="c-btn c-btn--icon-alone dropdown-trigger" id="options-dropdown-trigger-<%= tag.id %>" aria-haspopup="true" aria-expanded="false" aria-controls="options-dropdown">
<%= crayons_icon_tag("overflow-horizontal", title: t("views.dashboard.tags.options.icon")) %>
</button>
<div class="crayons-dropdown right-0 left-0 s:left-auto" id="options-dropdown-<%= tag.id %>">
<ul class="p-0">
<li>
<button type="button" class="hide-button c-btn w-100 align-left flex items-center" aria-label="Hide tag: <%= tag.name %>">
<%= crayons_icon_tag("eye-off", width: 18, height: 18, title: t("views.dashboard.tags.options.hide")) %> <span class="ml-2"><%= t("views.dashboard.tags.options.hide") %></span>
</button>
</li>
</ul>
</div>
</div>
</div>
<% end %>
<% if page === "hidden" %>
<div>
<button class="crayons-btn crayons-btn--danger unhide-button" aria-label="Unhide tag: <%= tag.name %>"><%= t("views.dashboard.tags.unhide_button") %></button>
</div>
<% end %>
</div>
</div>
<% end %>
<% end %>
</div>
<%= form_with url: bulk_update_follows_path, method: :patch, local: true, class: "sticky bg-base-inverted mt-3 pt-3 pl-3 pb-1 z-elevate", html: { style: "bottom: 0;margin-left:-1px" }, id: "follows_update_form" do |f| %>
<button type="submit" class="crayons-btn crayons-btn--m mb-3" name="commit"><%= t("views.dashboard.tags.update") %></button>
<% end %>

View file

@ -13,11 +13,7 @@
</div>
<div class="crayons-layout__content mb-5" id="user-dashboard">
<% if @followed_tags.any? %>
<div class="crayons-notice crayons-notice--info p-4 mx-3 s:mx-0 mb-3" aria-live="polite">
<%= t("views.dashboard.following_tags.adjust") %>
<span class="c-indicator c-indicator--warning ml-3"><%= t("views.dashboard.following_tags.default") %></span>
</div>
<%= render "tags", tags: @followed_tags %>
<%= render "tags", tags: @followed_tags, page: "following" %>
<% else %>
<div class="p-9 crayons-card crayons-card--secondary align-center fs-l h-100 flex items-center justify-center"><%= t("views.dashboard.following_tags.empty_html", link: tags_path) %></div>
<% end %>

View file

@ -13,10 +13,7 @@
</div>
<div class="crayons-layout__content mb-5" id="user-dashboard">
<% if @hidden_tags.any? %>
<div class="crayons-notice crayons-notice--info p-4 mx-3 s:mx-0 mb-3" aria-live="polite">
<%= t("views.dashboard.hidden_tags.adjust") %>
</div>
<%= render "tags", tags: @hidden_tags %>
<%= render "tags", tags: @hidden_tags, page: "hidden" %>
<% else %>
<div class="p-9 crayons-card crayons-card--secondary align-center fs-l h-100 flex items-center justify-center"><%= t("views.dashboard.hidden_tags.empty_html", link: tags_path) %></div>
<% end %>

View file

@ -5,7 +5,10 @@ json.array! @followed_tags do |follow|
json.type_of "tag_following"
json.extract!(follow, :id, :points, :explicit_points)
json.name followable.name
json.token form_authenticity_token
json.color Color::CompareHex.new(colors).brightness(0.8)
json.taggings_count followable.taggings_count
json.name followable.name
json.token form_authenticity_token
json.short_summary sanitize(followable.short_summary)
json.color Color::CompareHex.new(colors).brightness(0.8)
json.tag_id followable.id
end

View file

@ -108,20 +108,19 @@ en:
empty: You don't follow any podcasts yet...
following_tags:
heading: Dashboard » Following tags
adjust: Adjust the tag weight to modify your home feed. Higher values mean more appearances for that tag.
default: Default 1.0
empty_html: "<span>Follow tags to personalize your feed. You can explore popular tags <a href=%{link}>here</a></span>."
following_users:
heading: Dashboard » Following users
empty: You don't follow any users...
hidden_tags:
heading: Dashboard » Hidden tags
adjust: Adjust the tag weight to modify your home feed. Lower values mean fewer appearances for that tag.
empty_html: "<span>You don't have any hidden tags...</span>"
tags:
number:
aria_label: "%{name} tag weight"
update: Update Weights
following_button: Following
options:
icon: Options
hide: Hide tag
unhide_button: Unhide
listings:
meta:
title: Listings Dashboard

View file

@ -108,20 +108,19 @@ fr:
empty: You don't follow any podcasts yet...
following_tags:
heading: Dashboard » Following tags
adjust: Adjust the tag weight to modify your home feed. Higher values mean more appearances for that tag.
default: Default 1.0
empty_html: "<span>Follow tags to personalize your feed. You can explore popular tags <a href=%{link}>here</a></span>."
following_users:
heading: Tableau de bord » Utilisateurs suivis
empty: Vous ne suivez aucun utilisateur...
hidden_tags:
heading: Dashboard » Hidden tags
adjust: Adjust the tag weight to modify your home feed. Lower values mean fewer appearances for that tag.
empty_html: "<span>You don't have any hidden tags...</span>"
tags:
number:
aria_label: "%{name} tag weight"
update: Update Weights
following_button: Following
options:
icon: Options
hide: Hide tag
unhide_button: Unhide
listings:
meta:
title: Listings Dashboard

View file

@ -0,0 +1,101 @@
function openOptionsMenu(callback) {
cy.findAllByRole('button', { name: 'Options' })
.first()
.should('have.attr', 'aria-haspopup', 'true')
.should('have.attr', 'aria-expanded', 'false')
.click()
.then(([button]) => {
expect(button.getAttribute('aria-expanded')).to.equal('true');
const dropdownId = button.getAttribute('aria-controls');
cy.get(`#${dropdownId}`).within(callback);
});
}
describe('Dashboard: Following Tags', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/adminUser.json').as('user');
cy.get('@user').then((user) => {
cy.loginAndVisit(user, '/dashboard/following_tags').then(() => {
cy.findByRole('heading', { name: 'Dashboard » Following tags' });
});
});
});
it('shows the correct number of tags on the page', () => {
cy.get('.dashboard__tag__container').should('have.length', 5);
});
it('shows the appropriate buttons on the card', () => {
cy.findByRole('button', { name: 'Following tag: tag0' });
openOptionsMenu(() => {
cy.findByRole('button', { name: 'Hide tag: tag0' }).click();
});
});
it('unfollows a tag', () => {
cy.get('.dashboard__tag__container').should('have.length', 5);
cy.intercept('/follows').as('followsRequest');
cy.findByRole('button', { name: 'Following tag: tag0' }).as(
'followingButton',
);
cy.get('@followingButton').click();
cy.wait('@followsRequest');
// it removes the item from the 'Following tags' page
cy.get('.dashboard__tag__container').should('have.length', 4);
cy.findByRole('button', { name: 'Following tag: tag0' }).should(
'not.exist',
);
// it decreases the count from the 'Following tags' nav item
cy.get('.js-following-tags-link .c-indicator').as('followingTagsCount');
cy.get('@followingTagsCount').should('contain', '4');
});
it('hides a tag', () => {
cy.get('.dashboard__tag__container').should('have.length', 5);
cy.intercept('/follows').as('followsRequest');
openOptionsMenu(() => {
cy.findByRole('button', { name: 'Hide tag: tag0' }).as('hideButton');
});
cy.get('@hideButton').click();
cy.wait('@followsRequest');
// it removes the item from the 'Following tags' page
cy.get('.dashboard__tag__container').should('have.length', 4);
cy.findByRole('button', { name: 'Following tag: tag0' }).should(
'not.exist',
);
// it decreases the count from the 'Following tags' nav item
cy.get('.js-following-tags-link .c-indicator').as('followingTagsCount');
cy.get('@followingTagsCount').should('contain', '4');
// it increases the count from the 'Hidden tags' nav item
cy.get('.js-hidden-tags-link .c-indicator').as('hiddenTagsCount');
cy.get('@hiddenTagsCount').should('contain', '6');
});
it('shows the number of posts published for a tag', () => {
cy.get('.dashboard__tag__container')
.first()
.within(() => {
cy.findByText('0 posts');
});
cy.get('.dashboard__tag__container')
.eq(1)
.within(() => {
cy.findByText('1 posts');
});
});
// TODO: add a test for the pagination
});

View file

@ -0,0 +1,44 @@
describe('Dashboard: Hidden Tags', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/adminUser.json').as('user');
cy.get('@user').then((user) => {
cy.loginAndVisit(user, '/dashboard/hidden_tags').then(() => {
cy.findByRole('heading', { name: 'Dashboard » Hidden tags' });
});
});
});
it('shows the correct number of tags on the page', () => {
cy.get('.dashboard__tag__container').should('have.length', 5);
});
it('shows the appropriate buttons on the card', () => {
cy.findByRole('button', { name: 'Unhide tag: tag5' });
});
it('unhides a tag', () => {
cy.get('.dashboard__tag__container').should('have.length', 5);
cy.intercept('/follows').as('followsRequest');
cy.findByRole('button', { name: 'Unhide tag: tag5' }).as('unhideButton');
cy.get('@unhideButton').click();
cy.wait('@followsRequest');
// it removes the item from the 'Hidden tags' page
cy.get('.dashboard__tag__container').should('have.length', 4);
cy.findByRole('button', { name: 'Unhide tag: tag5' }).should('not.exist');
// it decreases the count from the 'Hidden tags' nav item
cy.get('.js-hidden-tags-link .c-indicator').as('hiddenTagsCount');
cy.get('@hiddenTagsCount').should('contain', '4');
// it decreases the count from the 'Following tags' nav item
cy.get('.js-following-tags-link .c-indicator').as('followingTagsCount');
cy.get('@followingTagsCount').should('contain', '6');
});
// TODO: add a test for the pagination
});

View file

@ -220,7 +220,8 @@ describe('Post Editor', () => {
cy.get('@articleForm')
.findByRole('textbox', { name: 'Add up to 4 tags' })
.as('postTags')
.type('tag1, tag2, tag3,');
.type('tag1, tag2, tag3,')
.type('{esc}');
getPostContent()
.should('have.value', `This is a Test Post's contents.`) // checking for original value first

View file

@ -79,7 +79,7 @@ RSpec.describe "FollowingsController" do
expect(response).to have_http_status(:ok)
expect(response.parsed_body.count).to eq(2)
expect(response.parsed_body.pluck("name")).to eq(%w[tagone tagthree])
expect(response.parsed_body.pluck("name")).to eq(%w[tagthree tagone])
end
it "returns the user's hidden tag list" do

View file

@ -3,6 +3,7 @@ require "rails_helper"
RSpec.describe "Follows #create" do
let(:current_user) { create(:user) }
let(:user) { create(:user) }
let(:tag) { create(:tag) }
let(:headers) { { "Content-Type": "application/json", Accept: "application/json" } }
let(:follow_payload) do
{
@ -11,6 +12,14 @@ RSpec.describe "Follows #create" do
verb: "follow"
}.to_json
end
let(:follow_tag_payload) do
{
followable_type: "Tag",
followable_id: tag.id,
verb: "follow",
explicit_points: -1
}.to_json
end
before do
sign_in current_user
@ -39,11 +48,22 @@ RSpec.describe "Follows #create" do
end
end
it "follows" do
post "/follows", headers: headers, params: follow_payload
context "when follows" do
it "returns followed" do
post "/follows", headers: headers, params: follow_payload
expect(response).to have_http_status(:ok)
expect(response.parsed_body["outcome"]).to eq("followed")
expect(response).to have_http_status(:ok)
expect(response.parsed_body["outcome"]).to eq("followed")
end
it "updates explicit points" do
post "/follows", headers: headers, params: follow_tag_payload
expect(response).to have_http_status(:ok)
expect(response.parsed_body["outcome"]).to eq("followed")
follow = Follow.find_by(followable_id: tag.id, followable_type: "ActsAsTaggableOn::Tag")
expect(follow.explicit_points).to eq(-1.0)
end
end
it "unfollows" do

View file

@ -941,17 +941,24 @@ end
##############################################################################
seeder.create_if_none(Tag) do
tags = %w[tag1 tag2]
tags.each do |tagname|
10.times do |i|
tag = Tag.create!(
name: tagname,
name: "tag#{i}",
short_summary: Faker::Hipster.paragraph(sentence_count: 2),
bg_color_hex: "#672c99",
text_color_hex: Faker::Color.hex_color,
supported: true,
)
admin_user.add_role(:tag_moderator, tag)
Follow.create(
followable_type: "ActsAsTaggableOn::Tag",
followable_id: tag.id,
follower_type: "User",
follower_id: admin_user.id,
explicit_points: i < 5 ? 1 : -1,
)
end
end

View file

@ -41,19 +41,6 @@ RSpec.describe "Infinite scroll on dashboard", js: true do
it "scrolls through all tags" do
page.assert_selector('div[id^="follows"]', count: total_records)
end
it "updates two tag point values" do
last_divs = page.all('div[id^="follows"]').last(2)
within(last_divs[0]) { fill_in with: 5.0, class: "crayons-textfield" }
within(last_divs[1]) { fill_in with: 10.0, class: "crayons-textfield" }
click_button "commit"
first_divs = page.all('div[id^="follows"]').first(2)
within(first_divs[0]) { expect(page).to have_field(with: 10.0, class: "crayons-textfield") }
within(first_divs[1]) { expect(page).to have_field(with: 5.0, class: "crayons-textfield") }
end
end
context "when /dashboard/following_users is visited" do