Skip to content
2 changes: 1 addition & 1 deletion packages/shared/src/components/Feed.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ describe('Feed logged in', () => {
await waitForNock();
expect(await screen.findByText('Happening Now')).toBeInTheDocument();
expect(screen.getByText('The first highlight')).toBeInTheDocument();
expect(screen.getByLabelText('Read all highlights')).toBeInTheDocument();
expect(screen.getByText('Read all')).toBeInTheDocument();
});

it('should keep feedV2 highlights in the response order', async () => {
Expand Down
131 changes: 37 additions & 94 deletions packages/shared/src/components/FeedItemComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { ReactElement } from 'react';
import React from 'react';
import { useQueryClient } from '@tanstack/react-query';
import type { AdSquadItem, FeedItem } from '../hooks/useFeed';
import { isBoostedPostAd, isBoostedSquadAd } from '../hooks/useFeed';
import { PlaceholderGrid } from './cards/placeholder/PlaceholderGrid';
Expand Down Expand Up @@ -56,13 +55,7 @@ import { OtherFeedPage } from '../lib/query';
import { isSourceSquadOrMachine } from '../graphql/sources';
import { HighlightGrid } from './cards/highlight/HighlightGrid';
import { HighlightList } from './cards/highlight/HighlightList';
import {
getHighlightIdsKey,
getHighlightIds,
MAJOR_HEADLINES_MAX_FIRST,
majorHeadlinesQueryOptions,
} from '../graphql/highlights';
import { HighlightPostModal } from './modals/HighlightPostModal';
import { getHighlightIds, getHighlightIdsKey } from '../graphql/highlights';

export type FeedItemComponentProps = {
item: FeedItem;
Expand Down Expand Up @@ -271,10 +264,6 @@ function FeedItemComponent({
disableAdRefresh,
}: FeedItemComponentProps): ReactElement | null {
const { logEvent } = useLogContext();
const queryClient = useQueryClient();
const [selectedHighlightId, setSelectedHighlightId] = React.useState<
string | null
>(null);
const inViewRef = useLogImpression(
item,
index,
Expand All @@ -294,90 +283,44 @@ function FeedItemComponent({
? HighlightList
: HighlightGrid;
const highlightIds = getHighlightIds(item.highlights);
const openHighlightModal = (highlightId: string): void => {
queryClient
.fetchQuery(
majorHeadlinesQueryOptions({ first: MAJOR_HEADLINES_MAX_FIRST }),
)
.catch(() => undefined)
.finally(() => setSelectedHighlightId(highlightId));
};

return (
<>
<HighlightTag
ref={inViewRef}
highlights={item.highlights}
onReadAllClick={() => {
const [firstHighlight] = item.highlights;

if (!firstHighlight) {
return;
}

logEvent(
feedHighlightsLogEvent(LogEvent.Click, {
columns: virtualizedNumCards,
column,
row,
feedName,
ranking,
action: 'read_all_click',
count: item.highlights.length,
highlightIds,
feedMeta: item.feedMeta,
}),
);

openHighlightModal(firstHighlight.id);
}}
onHighlightClick={(highlight, position) => {
logEvent(
feedHighlightsLogEvent(LogEvent.Click, {
columns: virtualizedNumCards,
column,
row,
feedName,
ranking,
action: 'highlight_click',
position,
count: item.highlights.length,
clickedHighlight: highlight,
highlightIds,
feedMeta: item.feedMeta,
}),
);

openHighlightModal(highlight.id);
}}
/>
<HighlightPostModal
isOpen={!!selectedHighlightId}
selectedHighlightId={selectedHighlightId}
highlights={item.highlights}
onRequestClose={() => setSelectedHighlightId(null)}
onHighlightClick={(highlight, position, modalHighlights) => {
logEvent(
feedHighlightsLogEvent(LogEvent.Click, {
columns: virtualizedNumCards,
column,
row,
feedName,
ranking,
action: 'modal_highlight_click',
position,
count: modalHighlights.length,
clickedHighlight: highlight,
highlightIds: getHighlightIds(modalHighlights),
feedMeta: item.feedMeta,
}),
);
}}
onSelectHighlight={(highlight) => {
setSelectedHighlightId(highlight.id);
}}
/>
</>
<HighlightTag
ref={inViewRef}
highlights={item.highlights}
onReadAllClick={() => {
logEvent(
feedHighlightsLogEvent(LogEvent.Click, {
columns: virtualizedNumCards,
column,
row,
feedName,
ranking,
action: 'read_all_click',
count: item.highlights.length,
highlightIds,
feedMeta: item.feedMeta,
}),
);
}}
onHighlightClick={(highlight, position) => {
logEvent(
feedHighlightsLogEvent(LogEvent.Click, {
columns: virtualizedNumCards,
column,
row,
feedName,
ranking,
action: 'highlight_click',
position,
count: item.highlights.length,
clickedHighlight: highlight,
highlightIds,
feedMeta: item.feedMeta,
}),
);
}}
/>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import userEvent from '@testing-library/user-event';
import { HighlightGrid } from './HighlightGrid';
import { HighlightList } from './HighlightList';

jest.mock('../../../lib/constants', () => ({
webappUrl: '/',
}));

const highlights = [
{
id: 'highlight-1',
Expand All @@ -28,7 +32,31 @@ const highlights = [
];

describe('Highlight cards', () => {
it('should render the grid card and trigger highlight actions', async () => {
it('should render the grid card with highlight links', () => {
render(<HighlightGrid highlights={highlights} />);

expect(screen.getByText('Happening Now')).toBeInTheDocument();
expect(screen.getByText('The first highlight')).toBeInTheDocument();
expect(screen.getByText('The second highlight')).toBeInTheDocument();
expect(screen.getByText('Read all')).toBeInTheDocument();
expect(
screen.getByRole('link', { name: /the first highlight/i }),
).toHaveAttribute('href', '/highlights?highlight=highlight-1');
expect(screen.getByLabelText('Read all highlights')).toHaveAttribute(
'href',
'/highlights?highlight=highlight-1',
);
});

it('should render the list card with highlight links', () => {
render(<HighlightList highlights={highlights} />);

expect(screen.getByText('The first highlight')).toBeInTheDocument();
expect(screen.getByText('The second highlight')).toBeInTheDocument();
expect(screen.getByText('Read all')).toBeInTheDocument();
});

it('should trigger the highlight callbacks without blocking navigation', async () => {
const onHighlightClick = jest.fn();
const onReadAllClick = jest.fn();

Expand All @@ -40,21 +68,12 @@ describe('Highlight cards', () => {
/>,
);

expect(screen.getByText('Happening Now')).toBeInTheDocument();

await userEvent.click(
screen.getByRole('button', { name: /the first highlight/i }),
screen.getByRole('link', { name: /the first highlight/i }),
);
await userEvent.click(screen.getByLabelText('Read all highlights'));

expect(onHighlightClick).toHaveBeenCalledWith(highlights[0], 1);
expect(onReadAllClick).toHaveBeenCalledTimes(1);
});

it('should render the list card', () => {
render(<HighlightList highlights={highlights} />);

expect(screen.getByText('The second highlight')).toBeInTheDocument();
expect(screen.getByLabelText('Read all highlights')).toBeInTheDocument();
});
});
15 changes: 2 additions & 13 deletions packages/shared/src/components/cards/highlight/HighlightGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import type { ReactElement, Ref } from 'react';
import React, { forwardRef } from 'react';
import classNames from 'classnames';
import { Card } from '../common/Card';
import type { HighlightCardProps } from './common';
import {
getHighlightCardContainerHandlers,
HighlightCardContent,
} from './common';
import { HighlightCardContent } from './common';

export const HighlightGrid = forwardRef(function HighlightGrid(
{ highlights, onHighlightClick, onReadAllClick }: HighlightCardProps,
Expand All @@ -16,14 +12,7 @@ export const HighlightGrid = forwardRef(function HighlightGrid(
<Card
ref={ref}
data-testid="highlightItem"
role={onReadAllClick ? 'button' : undefined}
tabIndex={onReadAllClick ? 0 : undefined}
aria-label={onReadAllClick ? 'Open highlights card' : undefined}
{...getHighlightCardContainerHandlers(onReadAllClick)}
className={classNames(
'group flex h-full flex-col overflow-hidden !bg-surface-float hover:!bg-surface-float',
onReadAllClick && 'cursor-pointer',
)}
className="group flex h-full flex-col overflow-hidden !bg-surface-float hover:!bg-surface-float"
>
<HighlightCardContent
highlights={highlights}
Expand Down
15 changes: 2 additions & 13 deletions packages/shared/src/components/cards/highlight/HighlightList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import type { ReactElement, Ref } from 'react';
import React, { forwardRef } from 'react';
import classNames from 'classnames';
import { ListCard } from '../common/list/ListCard';
import type { HighlightCardProps } from './common';
import {
getHighlightCardContainerHandlers,
HighlightCardContent,
} from './common';
import { HighlightCardContent } from './common';

export const HighlightList = forwardRef(function HighlightList(
{ highlights, onHighlightClick, onReadAllClick }: HighlightCardProps,
Expand All @@ -16,14 +12,7 @@ export const HighlightList = forwardRef(function HighlightList(
<ListCard
ref={ref}
data-testid="highlightItem"
role={onReadAllClick ? 'button' : undefined}
tabIndex={onReadAllClick ? 0 : undefined}
aria-label={onReadAllClick ? 'Open highlights card' : undefined}
{...getHighlightCardContainerHandlers(onReadAllClick)}
className={classNames(
'group overflow-hidden !border-0 !border-t !border-border-subtlest-tertiary !bg-gradient-to-b !from-surface-float !to-background-default !px-4 !py-6',
onReadAllClick && 'cursor-pointer',
)}
className="group overflow-hidden !border-0 !border-t !border-border-subtlest-tertiary !bg-gradient-to-b !from-surface-float !to-background-default !px-4 !py-6"
>
<HighlightCardContent
highlights={highlights}
Expand Down
Loading
Loading