Skip to content

Commit

Permalink
fix(webapp): Articles: Anon users: Disable "reacting" & "show reactio…
Browse files Browse the repository at this point in the history
…ns" buttons

Fixes #368
  • Loading branch information
ptitmouton committed Sep 11, 2024
1 parent 4ebd673 commit 1844e7d
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 121 deletions.
138 changes: 93 additions & 45 deletions apps/webapp/src/article/articleReactions/ArticleReactions.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { render, waitFor } from 'test/util';
import { Weihnachtsmarkt } from 'test/fixtures';
import { SomeUser, Weihnachtsmarkt } from 'test/fixtures';
import { ArticleReactions } from './ArticleReactions';
import userEvent from '@testing-library/user-event';

import GetArticleReactionCounts from 'api/query/GetArticleReactionCounts.graphql';
import GetReactionUsersQuery from 'api/query/GetReactionUsersQuery.graphql';
import ReactToArticleMutation from 'api/mutation/ReactToArticleMutation.graphql';

const additionalMocks = [
Expand Down Expand Up @@ -31,12 +32,24 @@ const additionalMocks = [
},
result: {
data: {
reactToArticle: {
success: true,
article: {
...Weihnachtsmarkt,
reactionCounts: [{ type: 'PEPPER', count: 6 }],
},
},
},
},
{
request: {
query: GetReactionUsersQuery,
variables: { id: Weihnachtsmarkt.id, reaction: 'PEPPER' },
},
result: {
data: {
users: [SomeUser],
},
},
},
];

describe('ArticleReactions Component', () => {
Expand All @@ -46,66 +59,101 @@ describe('ArticleReactions Component', () => {
{},
{
additionalMocks,
currentUser: SomeUser,
}
);

// Verify the component renders with initial data
await waitFor(() => {
expect(screen.getByTestId('ArticleReactions')).toBeInTheDocument();
expect(
screen.getByTitle(`Auf "${Weihnachtsmarkt.title}" reagieren`)
).toBeInTheDocument();
});
});

it('opens reaction selector when button is clicked', async () => {
const user = userEvent.setup();
const screen = render(
<ArticleReactions article={Weihnachtsmarkt} />,
{},
{ additionalMocks }
);
describe('adding a reaction', () => {
it('opens reaction selector when button is clicked', async () => {
const user = userEvent.setup();
const screen = render(
<ArticleReactions article={Weihnachtsmarkt} />,
{},
{ additionalMocks, currentUser: SomeUser }
);

const reactionButton = await screen.findByTitle(
`Auf "${Weihnachtsmarkt.title}" reagieren`
);
await user.click(reactionButton);
const reactionButton = await screen.findByTitle(
`Auf "${Weihnachtsmarkt.title}" reagieren`
);
await user.click(reactionButton);

// Assert that the reaction selector opens
await waitFor(() =>
expect(screen.getByTestId('ReactionSelector')).toBeInTheDocument()
);
});
// Assert that the reaction selector opens
await waitFor(() =>
expect(screen.getByTestId('ReactionSelector')).toBeInTheDocument()
);
});

it('allows selecting and submitting a reaction', async () => {
const user = userEvent.setup();
const screen = render(
<ArticleReactions article={Weihnachtsmarkt} />,
{},
{ additionalMocks }
);
it('allows selecting and submitting a reaction', async () => {
const user = userEvent.setup();
const screen = render(
<ArticleReactions article={Weihnachtsmarkt} />,
{},
{ additionalMocks, currentUser: SomeUser }
);

const reactionButton = await screen.findByTitle(
`Auf "${Weihnachtsmarkt.title}" reagieren`
);
await user.click(reactionButton);
const reactionButton = await screen.findByTitle(
`Auf "${Weihnachtsmarkt.title}" reagieren`
);
await user.click(reactionButton);

const pepperReactionButton = screen.getByTestId('reaction-PEPPER');
await user.click(pepperReactionButton);

await waitFor(() => expect(pepperReactionButton).not.toBeInTheDocument());
});

it('should not show the reaction button when the user is not logged in', async () => {
const screen = render(
<ArticleReactions article={Weihnachtsmarkt} />,
{},
{ additionalMocks }
);

const pepperReactionButton = screen.getByTestId('reaction-PEPPER');
await user.click(pepperReactionButton);
await screen.findByTestId('ArticleReactions');

await waitFor(() => expect(pepperReactionButton).not.toBeInTheDocument());
expect(
screen.queryByTitle(`Auf "${Weihnachtsmarkt.title}" reagieren`)
).toBeNull();
});
});

it('shows reaction count buttons', async () => {
const screen = render(
<ArticleReactions article={Weihnachtsmarkt} />,
{},
{ additionalMocks }
);
describe('show reactions', () => {
it('open reaction count list when clicking on reaction button', async () => {
const user = userEvent.setup();
const screen = render(
<ArticleReactions article={Weihnachtsmarkt} />,
{},
{ additionalMocks, currentUser: SomeUser }
);

await waitFor(() => {
expect(screen.getByText('5')).toBeInTheDocument();
expect(screen.getByText('3')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByRole('button', { name: /5/ })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /3/ })).toBeInTheDocument();
});

const reactionButton = screen.getByRole('button', { name: /5/ });
await user.click(reactionButton);

expect(await screen.findByTestId('ReactionUserList')).toBeVisible();
});

it('should disable the reaction button when the user is not logged in', async () => {
const screen = render(
<ArticleReactions article={Weihnachtsmarkt} />,
{},
{ additionalMocks }
);

await screen.findByTestId('ArticleReactions');

expect(screen.getByRole('button', { name: /5/ })).toBeDisabled();
expect(screen.getByRole('button', { name: /3/ })).toBeDisabled();
});
});
});
155 changes: 85 additions & 70 deletions apps/webapp/src/article/articleReactions/ArticleReactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,26 @@ import {
import { useMutation, useSuspenseQuery } from '@apollo/client';
import { ArticleModel, ArticleReactionType } from 'model';
import { Icon } from 'shared/Icon';
import { ReactionSelector } from './ReactionSelector';
import { useCurrentUser } from 'util/user';
import { ReactionCountButtons } from './ReactionCountButtons';
import { iconForReactionType } from './supportedReactionIcons';
import { ReactionUserList } from './RactionUserList';
import dynamic from 'next/dynamic';

import styles from './ArticleReactions.module.scss';

import GetArticleReactionCounts from 'api/query/GetArticleReactionCounts.graphql';
import ReactToArticleMutation from 'api/mutation/ReactToArticleMutation.graphql';

const DynamicReactionSelector = dynamic(() => import('./ReactionSelector'));

export type ArticleReactionsProps = {
article: ArticleModel;
};

export const ArticleReactions = React.memo(
({ article }: ArticleReactionsProps) => {
const currentUser = useCurrentUser();
const ref = React.useRef<HTMLDivElement>(null);
const buttonRef = React.useRef<HTMLButtonElement>(null);
const typeButtonRef = React.useRef<HTMLButtonElement | null>(null);
Expand Down Expand Up @@ -85,71 +89,56 @@ export const ArticleReactions = React.memo(

return (
<div data-testid="ArticleReactions" className={styles.root} ref={ref}>
<ReactionSelector
trigger={buttonRef.current!}
isOpen={isReactionSelectorOpen}
onSelect={(reaction) => {
if (reaction) {
reactToArticle({
variables: {
id: article.id,
reaction: reaction.toUpperCase(),
},
});
}
setIsReactionSelectorOpen(false);
}}
/>
<Button
ref={buttonRef}
title={`Auf "${article.title}" reagieren`}
icon={<Icon icon={faHeart} />}
onClick={() => setIsReactionSelectorOpen(true)}
/>
{currentUser && (
<>
<DynamicReactionSelector
trigger={buttonRef.current!}
isOpen={isReactionSelectorOpen}
onSelect={(reaction) => {
if (reaction) {
reactToArticle({
variables: {
id: article.id,
reaction: reaction.toUpperCase(),
},
});
}
setIsReactionSelectorOpen(false);
}}
/>
<Button
ref={buttonRef}
title={`Auf "${article.title}" reagieren`}
disabled={!currentUser}
icon={<Icon icon={faHeart} />}
onClick={() => setIsReactionSelectorOpen(true)}
/>
</>
)}
<ReactionCountButtons
reactions={reactionCounts}
onSelect={(type, el) => {
typeButtonRef.current = el;
setSelectedReactionType(type);
}}
onSelect={
currentUser
? (type, el) => {
typeButtonRef.current = el;
setSelectedReactionType(type);
}
: undefined
}
/>
<Popover
isOpen={!!selectedReactionType}
trigger={typeButtonRef.current!}
placement={'top-start'}
modifiers={popperModifiers}
onClose={() => setSelectedReactionType(null)}
>
<Overlay>
<React.Suspense
fallback={
<List>
{selectedReactionTypeIcon && (
<ListItem
isHeader
leftSection={
<Icon
icon={
iconForReactionType(selectedReactionType!)!.icon
}
/>
}
>
{selectedReactionTypeCount}
</ListItem>
)}
<ListItem>
<LinearProgress isIndeterminate />
</ListItem>
</List>
}
>
{selectedReactionType && (
<ReactionUserList
articleId={article.id}
reaction={selectedReactionType}
header={
selectedReactionTypeIcon && (
{currentUser && (
<Popover
isOpen={!!selectedReactionType}
trigger={typeButtonRef.current!}
placement={'top-start'}
modifiers={popperModifiers}
onClose={() => setSelectedReactionType(null)}
>
<Overlay>
<React.Suspense
fallback={
<List>
{selectedReactionTypeIcon && (
<ListItem
isHeader
leftSection={
Expand All @@ -162,13 +151,39 @@ export const ArticleReactions = React.memo(
>
{selectedReactionTypeCount}
</ListItem>
)
}
/>
)}
</React.Suspense>
</Overlay>
</Popover>
)}
<ListItem>
<LinearProgress isIndeterminate />
</ListItem>
</List>
}
>
{selectedReactionType && (
<ReactionUserList
articleId={article.id}
reaction={selectedReactionType}
header={
selectedReactionTypeIcon && (
<ListItem
isHeader
leftSection={
<Icon
icon={
iconForReactionType(selectedReactionType!)!.icon
}
/>
}
>
{selectedReactionTypeCount}
</ListItem>
)
}
/>
)}
</React.Suspense>
</Overlay>
</Popover>
)}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const ReactionUserList = React.memo(
variables: { id: articleId, reaction },
});
return (
<Overlay>
<Overlay data-testid="ReactionUserList">
{header || null}
<List>
{users.map((user) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const ReactionCountButtons = ({
key={type}
className={styles.button}
icon={<Icon icon={icon!.icon} />}
disabled={!count || !onSelect}
onClick={(e: React.MouseEvent<HTMLButtonElement>) =>
onSelect?.(type, e.currentTarget)
}
Expand Down
Loading

0 comments on commit 1844e7d

Please sign in to comment.