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:
parent
5ef0f43636
commit
5266dd1afb
20 changed files with 486 additions and 113 deletions
|
|
@ -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>`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
178
app/javascript/packs/dashboardTags.js
Normal file
178
app/javascript/packs/dashboardTags.js
Normal 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();
|
||||
});
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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") %>
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
101
cypress/e2e/seededFlows/dashboardFlows/followingTags.spec.js
Normal file
101
cypress/e2e/seededFlows/dashboardFlows/followingTags.spec.js
Normal 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
|
||||
});
|
||||
44
cypress/e2e/seededFlows/dashboardFlows/hiddenTags.spec.js
Normal file
44
cypress/e2e/seededFlows/dashboardFlows/hiddenTags.spec.js
Normal 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
|
||||
});
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue