From c25e5caf832d481f302dfc4c98269b9c530e1a07 Mon Sep 17 00:00:00 2001
From: Ido Shamun <1993245+idoshamun@users.noreply.github.com>
Date: Tue, 25 Aug 2026 23:40:24 +0300
Subject: [PATCH] feat(shared): link to a user's world from the profile tooltip
Adds an icon-only world button next to Follow in the user card the profile
tooltip renders, so the world is reachable from anywhere a user is hovered
rather than only from their profile page.
Shown for every user, including the viewer themselves (who has no Follow
button). The card has no cheap way to know whether a world has anything in
it, and the world route already handles the empty and private cases.
---
.../cards/entity/UserEntityCard.spec.tsx | 67 +++++++++++++++++++
.../cards/entity/UserEntityCard.tsx | 34 +++++++---
2 files changed, 90 insertions(+), 11 deletions(-)
create mode 100644 packages/shared/src/components/cards/entity/UserEntityCard.spec.tsx
diff --git a/packages/shared/src/components/cards/entity/UserEntityCard.spec.tsx b/packages/shared/src/components/cards/entity/UserEntityCard.spec.tsx
new file mode 100644
index 00000000000..945ba544361
--- /dev/null
+++ b/packages/shared/src/components/cards/entity/UserEntityCard.spec.tsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import UserEntityCard from './UserEntityCard';
+import AuthContext from '../../../contexts/AuthContext';
+import type { LoggedUser, UserShortProfile } from '../../../lib/user';
+import { webappUrl } from '../../../lib/constants';
+
+jest.mock(
+ '../../../hooks/contentPreference/useContentPreferenceStatusQuery',
+ () => ({
+ useContentPreferenceStatusQuery: () => ({ data: undefined }),
+ }),
+);
+
+jest.mock('../../../hooks/useShowFollowAction', () => ({
+ __esModule: true,
+ default: () => ({ isLoading: false, showActionBtn: true }),
+}));
+
+const user: UserShortProfile = {
+ id: 'u1',
+ username: 'johndoe',
+ name: 'John Doe',
+ image: 'https://daily.dev/john.jpg',
+ permalink: 'https://daily.dev/johndoe',
+ createdAt: '2024-01-01T00:00:00.000Z',
+ bio: 'hello',
+ reputation: 10,
+} as UserShortProfile;
+
+const renderComponent = (loggedUserId?: string) =>
+ render(
+
+
+
+
+ ,
+ );
+
+describe('UserEntityCard', () => {
+ it('links to the user world', () => {
+ renderComponent('other');
+
+ const link = screen.getByLabelText("Visit @johndoe's world");
+ expect(link).toHaveAttribute('href', `${webappUrl}world/johndoe`);
+ });
+
+ it('keeps the world link for the logged in user, who has no follow button', () => {
+ renderComponent('u1');
+
+ expect(screen.getByLabelText("Visit @johndoe's world")).toBeInTheDocument();
+ expect(screen.queryByText('Follow')).not.toBeInTheDocument();
+ });
+});
diff --git a/packages/shared/src/components/cards/entity/UserEntityCard.tsx b/packages/shared/src/components/cards/entity/UserEntityCard.tsx
index 2dcb7739748..c4ae5c110fc 100644
--- a/packages/shared/src/components/cards/entity/UserEntityCard.tsx
+++ b/packages/shared/src/components/cards/entity/UserEntityCard.tsx
@@ -9,7 +9,7 @@ import {
TypographyTag,
TypographyType,
} from '../../typography/Typography';
-import { DevPlusIcon } from '../../icons';
+import { DevPlusIcon, WorldIcon } from '../../icons';
import { VerifiedCompanyUserBadge } from '../../VerifiedCompanyUserBadge';
import { ProfileImageSize } from '../../ProfilePicture';
import { ReputationUserBadge } from '../../ReputationUserBadge';
@@ -19,9 +19,10 @@ import { FollowButton } from '../../contentPreference/FollowButton';
import { ContentPreferenceType } from '../../../graphql/contentPreference';
import { useContentPreferenceStatusQuery } from '../../../hooks/contentPreference/useContentPreferenceStatusQuery';
import AuthContext from '../../../contexts/AuthContext';
-import { ButtonVariant } from '../../buttons/Button';
+import { Button, ButtonSize, ButtonVariant } from '../../buttons/Button';
import EntityDescription from './EntityDescription';
import useShowFollowAction from '../../../hooks/useShowFollowAction';
+import { webappUrl } from '../../../lib/constants';
type Props = {
user?: UserShortProfile;
@@ -48,6 +49,7 @@ const UserEntityCard = ({ user, className }: Props) => {
const { username, bio, name, image, isPlus, createdAt, id, permalink } = user;
const isSameUser = loggedUser?.id === id;
const showActionBtns = !isLoading && !isSameUser;
+ const worldHref = `${webappUrl}world/${username}`;
return (
{
}}
entityName={username}
actionButtons={
- showActionBtns && (
-
+ }
+ size={ButtonSize.Small}
+ variant={ButtonVariant.Secondary}
/>
- )
+ {showActionBtns && (
+
+ )}
+ >
}
>