mirror of
https://github.com/discourse/discourse.git
synced 2026-08-06 03:07:17 -05:00
UX: Migrate core likes post action menu to show likes with DMenu (#34265)
This commit is contained in:
@@ -337,6 +337,15 @@ nav.post-controls {
|
||||
text-align: right;
|
||||
margin-left: auto;
|
||||
|
||||
.post-action-menu__double-button {
|
||||
.discourse-no-touch & {
|
||||
&:not(.has-liked):hover {
|
||||
--d-post-control-background--hover: var(--love-low);
|
||||
--d-button-flat-icon-color--hover: var(--love);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some buttons can be doubled up, like likes or flags
|
||||
.double-button {
|
||||
display: inline-flex;
|
||||
@@ -370,28 +379,11 @@ nav.post-controls {
|
||||
border-radius: var(--d-post-control-border-radius);
|
||||
}
|
||||
|
||||
&.like {
|
||||
// Like button with 0 likes
|
||||
&:hover {
|
||||
.d-icon {
|
||||
color: var(--love);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.has-like {
|
||||
// Like button after I've liked
|
||||
.d-icon {
|
||||
color: var(--love);
|
||||
}
|
||||
|
||||
.discourse-no-touch & {
|
||||
&:hover {
|
||||
.d-icon {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.button-count {
|
||||
@@ -400,6 +392,10 @@ nav.post-controls {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&.my-likes {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
+ .toggle-like {
|
||||
// Like button when like count is present
|
||||
padding-left: 0.45em;
|
||||
@@ -1732,3 +1728,59 @@ html.discourse-no-touch .fullscreen-table-wrapper:hover {
|
||||
opacity ease-out 1s;
|
||||
}
|
||||
}
|
||||
|
||||
.liked-users-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
|
||||
&__list {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
gap: var(--space-1);
|
||||
|
||||
// avatar width + gap * (max amount of users per row + 1)
|
||||
max-width: calc((var(--liked-users-list-avatar-size) + var(--space-1)) * 9);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&__item,
|
||||
&__item .avatar {
|
||||
width: var(--liked-users-list-avatar-size);
|
||||
height: var(--liked-users-list-avatar-size);
|
||||
}
|
||||
|
||||
&__container {
|
||||
--liked-users-list-avatar-size: 22px;
|
||||
padding: var(--space-2);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
&__count {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
color: var(--primary-medium);
|
||||
font-weight: 300;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__count-icon {
|
||||
color: var(--love);
|
||||
}
|
||||
|
||||
&__more {
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
||||
&__more-button.btn-flat.btn {
|
||||
padding: 0;
|
||||
width: var(--liked-users-list-avatar-size);
|
||||
border-radius: var(--d-button-border-radius);
|
||||
height: var(--liked-users-list-avatar-size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { action } from "@ember/object";
|
||||
import { getOwner } from "@ember/owner";
|
||||
import { service } from "@ember/service";
|
||||
import { isEmpty, isPresent } from "@ember/utils";
|
||||
import { and, eq } from "truth-helpers";
|
||||
import { and } from "truth-helpers";
|
||||
import AdminPostMenu from "discourse/components/admin-post-menu";
|
||||
import DeleteTopicDisallowedModal from "discourse/components/modal/delete-topic-disallowed";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
@@ -672,22 +672,6 @@ export default class PostMenu extends Component {
|
||||
}}
|
||||
@users={{this.readers}}
|
||||
/>
|
||||
<SmallUserList
|
||||
class="who-liked"
|
||||
@addSelf={{and @post.liked (eq this.remainingLikedUsers 0)}}
|
||||
@isVisible={{this.isWhoLikedVisible}}
|
||||
@count={{if
|
||||
this.remainingLikedUsers
|
||||
this.remainingLikedUsers
|
||||
this.totalLikedUsers
|
||||
}}
|
||||
@description={{if
|
||||
this.remainingLikedUsers
|
||||
"post.actions.people.like_capped"
|
||||
"post.actions.people.like"
|
||||
}}
|
||||
@users={{this.likedUsers}}
|
||||
/>
|
||||
{{#if
|
||||
(this.showMoreButton.shouldRender
|
||||
(hash post=this.post state=this.state)
|
||||
|
||||
@@ -4,10 +4,9 @@ import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import discourseLater from "discourse/lib/later";
|
||||
import { applyValueTransformer } from "discourse/lib/transformer";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import LikedUsersList from "../liked-users-list";
|
||||
|
||||
export default class PostMenuLikeButton extends Component {
|
||||
static shouldRender(args) {
|
||||
@@ -61,13 +60,16 @@ export default class PostMenuLikeButton extends Component {
|
||||
|
||||
<template>
|
||||
{{#if @post.showLike}}
|
||||
<div class="double-button">
|
||||
<LikeCount
|
||||
...attributes
|
||||
@action={{@buttonActions.toggleWhoLiked}}
|
||||
@state={{@state}}
|
||||
@post={{@post}}
|
||||
/>
|
||||
<div
|
||||
class={{concatClass
|
||||
"double-button"
|
||||
(if @post.liked "has-liked" "")
|
||||
"post-action-menu__double-button"
|
||||
}}
|
||||
>
|
||||
{{#if @post.likeCount}}
|
||||
<LikedUsersList ...attributes @post={{@post}} />
|
||||
{{/if}}
|
||||
<DButton
|
||||
class={{concatClass
|
||||
"post-action-menu__like"
|
||||
@@ -87,76 +89,8 @@ export default class PostMenuLikeButton extends Component {
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="double-button">
|
||||
<LikeCount
|
||||
...attributes
|
||||
@action={{@buttonActions.toggleWhoLiked}}
|
||||
@state={{@state}}
|
||||
@post={{@post}}
|
||||
/>
|
||||
<LikedUsersList ...attributes @post={{@post}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
||||
class LikeCount extends Component {
|
||||
get icon() {
|
||||
if (!this.args.post.showLike) {
|
||||
return this.args.post.yours ? "d-liked" : "d-unliked";
|
||||
}
|
||||
|
||||
if (this.args.post.yours) {
|
||||
return "d-liked";
|
||||
}
|
||||
}
|
||||
|
||||
get translatedTitle() {
|
||||
let title;
|
||||
|
||||
if (this.args.post.liked) {
|
||||
title =
|
||||
this.args.post.likeCount === 1
|
||||
? "post.has_likes_title_only_you"
|
||||
: "post.has_likes_title_you";
|
||||
} else {
|
||||
title = "post.has_likes_title";
|
||||
}
|
||||
|
||||
return i18n(title, {
|
||||
count: this.args.post.liked
|
||||
? this.args.post.likeCount - 1
|
||||
: this.args.post.likeCount,
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if @post.likeCount}}
|
||||
<DButton
|
||||
class={{concatClass
|
||||
"post-action-menu__like-count"
|
||||
"like-count"
|
||||
"button-count"
|
||||
"highlight-action"
|
||||
(if @post.yours "my-likes" "regular-likes")
|
||||
}}
|
||||
...attributes
|
||||
@ariaPressed={{@state.isWhoLikedVisible}}
|
||||
@translatedAriaLabel={{i18n
|
||||
"post.sr_post_like_count_button"
|
||||
count=@post.likeCount
|
||||
}}
|
||||
@translatedTitle={{this.translatedTitle}}
|
||||
@action={{@action}}
|
||||
>
|
||||
{{@post.likeCount}}
|
||||
{{!--
|
||||
When displayed, the icon on the Like Count button is aligned to the right
|
||||
To get the desired effect will use the {{yield}} in the DButton component to our advantage
|
||||
introducing manually the icon after the label
|
||||
--}}
|
||||
{{#if this.icon}}
|
||||
{{~icon this.icon~}}
|
||||
{{/if}}
|
||||
</DButton>
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import UserAvatar from "discourse/components/user-avatar";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DMenu from "float-kit/components/d-menu";
|
||||
|
||||
const LIKE_ACTION = 2; // The action type ID for "like" in Discourse
|
||||
const DISPLAY_MAX_USERS = 8; // will show X users, then a button to show one more row of X;
|
||||
|
||||
export default class LikedUsersList extends Component {
|
||||
@service store;
|
||||
|
||||
@tracked likedUsers;
|
||||
@tracked loadingLikedUsers = false;
|
||||
@tracked slicedUsersVisible = false;
|
||||
|
||||
@action
|
||||
async fetchLikedUsers() {
|
||||
if (this.loadingLikedUsers) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadingLikedUsers = true;
|
||||
|
||||
try {
|
||||
this.likedUsers = await this.store.find("post-action-user", {
|
||||
id: this.args.post.id,
|
||||
post_action_type_id: LIKE_ACTION,
|
||||
});
|
||||
} finally {
|
||||
this.loadingLikedUsers = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
toggleSlicedUsersVisiblity() {
|
||||
this.slicedUsersVisible = !this.slicedUsersVisible;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
if (!this.args.post.showLike) {
|
||||
return this.args.post.yours ? "d-liked" : "d-unliked";
|
||||
}
|
||||
|
||||
if (this.args.post.yours) {
|
||||
return "d-liked";
|
||||
}
|
||||
}
|
||||
|
||||
get truncatedUsers() {
|
||||
return this.likedUsers?.content.slice(0, DISPLAY_MAX_USERS);
|
||||
}
|
||||
|
||||
get slicedUsers() {
|
||||
return this.likedUsers?.content.slice(
|
||||
DISPLAY_MAX_USERS,
|
||||
DISPLAY_MAX_USERS * 2
|
||||
);
|
||||
}
|
||||
|
||||
get hiddenUserCount() {
|
||||
return (
|
||||
this.likedUsers?.length -
|
||||
(this.truncatedUsers.length + this.slicedUsers.length)
|
||||
);
|
||||
}
|
||||
|
||||
get toggleSlicedUsersVisiblityIcon() {
|
||||
return this.slicedUsersVisible ? "angle-up" : "angle-down";
|
||||
}
|
||||
|
||||
<template>
|
||||
<DMenu
|
||||
@modalForMobile={{true}}
|
||||
@identifier="post-like-users_{{@post.id}}"
|
||||
@onShow={{this.fetchLikedUsers}}
|
||||
@triggerClass={{concatClass
|
||||
"post-action-menu__like-count"
|
||||
"like-count"
|
||||
"btn-flat"
|
||||
"button-count"
|
||||
"highlight-action"
|
||||
(if @post.yours "my-likes" "regular-likes")
|
||||
}}
|
||||
@icon={{if @post.yours "d-liked" ""}}
|
||||
@placement="top"
|
||||
label={{i18n "post.sr_post_like_count_button" count=@post.likeCount}}
|
||||
id="post-like-users_{{@post.id}}"
|
||||
>
|
||||
<:trigger>
|
||||
{{@post.likeCount}}
|
||||
</:trigger>
|
||||
<:content>
|
||||
<ConditionalLoadingSpinner
|
||||
@condition={{this.loadingLikedUsers}}
|
||||
class="liked-users-list__container"
|
||||
>
|
||||
<span class="liked-users-list__count">
|
||||
{{icon "d-liked" class="liked-users-list__count-icon"}}
|
||||
{{@post.likeCount}}
|
||||
</span>
|
||||
<div class="liked-users-list">
|
||||
<ul class="liked-users-list__list">
|
||||
{{#each this.truncatedUsers as |user|}}
|
||||
<li class="liked-users-list__item">
|
||||
<UserAvatar
|
||||
class="trigger-user-card"
|
||||
@user={{user}}
|
||||
@size="small"
|
||||
/>
|
||||
</li>
|
||||
{{/each}}
|
||||
{{#if this.slicedUsers}}
|
||||
<li class="liked-users-list__item">
|
||||
<DButton
|
||||
class="liked-users-list__more-button btn-flat"
|
||||
@icon={{this.toggleSlicedUsersVisiblityIcon}}
|
||||
@action={{this.toggleSlicedUsersVisiblity}}
|
||||
/>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
{{#if this.slicedUsersVisible}}
|
||||
<ul class="liked-users-list__list">
|
||||
{{#each this.slicedUsers as |user|}}
|
||||
<li class="liked-users-list__item">
|
||||
<UserAvatar
|
||||
class="trigger-user-card"
|
||||
@user={{user}}
|
||||
@size="small"
|
||||
/>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{#if this.hiddenUserCount}}
|
||||
<span class="liked-users-list__more">
|
||||
{{i18n
|
||||
"discourse_reactions.state_panel.more_users"
|
||||
count=this.hiddenUserCount
|
||||
}}
|
||||
</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</ConditionalLoadingSpinner>
|
||||
</:content>
|
||||
</DMenu>
|
||||
</template>
|
||||
}
|
||||
@@ -4,32 +4,17 @@ import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
acceptance(`Post controls`, function () {
|
||||
test("accessibility of the likes list below the post", async function (assert) {
|
||||
test("menu of like count is shown when clicking on like count", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
assert
|
||||
.dom("#post_2 button.like-count")
|
||||
.hasAria("pressed", "false", "show likes button isn't pressed");
|
||||
assert.dom("#post_2 .button-count").exists("like count button exists");
|
||||
|
||||
await click("#post_2 button.like-count");
|
||||
assert
|
||||
.dom("#post_2 button.like-count")
|
||||
.hasAria("pressed", "true", "show likes button is now pressed");
|
||||
await click("#post_2 .button-count");
|
||||
|
||||
// Assert that the liked users list container appears
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked .small-user-list-content")
|
||||
.hasAttribute("role", "list", "likes container has list role");
|
||||
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.exists("avatars are rendered");
|
||||
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.hasAria("hidden", "false", "avatars are not aria-hidden");
|
||||
assert
|
||||
.dom("#post_2 .small-user-list.who-liked a.trigger-user-card")
|
||||
.hasAttribute("role", "listitem", "avatars have listitem role");
|
||||
.dom(".liked-users-list__container")
|
||||
.exists("liked users list container appears");
|
||||
});
|
||||
|
||||
test("accessibility of the embedded replies below the post", async function (assert) {
|
||||
|
||||
@@ -130,9 +130,10 @@ module PageObjects
|
||||
def has_who_liked_on_post?(post, count: nil)
|
||||
if count
|
||||
return(
|
||||
within_post(post) do
|
||||
has_css?(".who-liked.--expanded a.trigger-user-card", count: count)
|
||||
end
|
||||
has_css?(
|
||||
".liked-users-list__container .liked-users-list__item a.trigger-user-card",
|
||||
count: count,
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -487,21 +487,15 @@ describe "Post menu", type: :system do
|
||||
expect(topic_page).to have_no_post_action_button(post2, :like_count)
|
||||
end
|
||||
|
||||
it "toggles the users who liked when clicking on the like count" do
|
||||
it "shows the users who liked when clicking on the like count" do
|
||||
PostActionCreator.like(user, post)
|
||||
PostActionCreator.like(admin, post)
|
||||
|
||||
topic_page.visit_topic(post.topic)
|
||||
|
||||
expect(topic_page).to have_no_who_liked_on_post(post)
|
||||
|
||||
# toggle users who liked on
|
||||
# show users who liked on
|
||||
topic_page.click_post_action_button(post, :like_count)
|
||||
expect(topic_page).to have_who_liked_on_post(post, count: 2)
|
||||
|
||||
# toggle users who liked off
|
||||
topic_page.click_post_action_button(post, :like_count)
|
||||
expect(topic_page).to have_no_who_liked_on_post(post)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user