Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 38 additions & 12 deletions dotcom-rendering/src/components/ArticleMeta.apps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { Dateline } from './Dateline';
import { FollowWrapper } from './FollowWrapper.island';
import { Island } from './Island';
import { ListenToArticle } from './ListenToArticle.island';
import { LiveblogNotifications } from './LiveblogNotifications.island';
import { NotificationsToggle } from './NotificationsToggle.island';

type Props = {
format: ArticleFormat;
Expand Down Expand Up @@ -249,9 +249,6 @@ export const ArticleMetaApps = ({
const shouldShowFollowButtons = (layoutOrDesignType: boolean) =>
layoutOrDesignType && !!byline && !isUndefined(soleContributor);

const shouldShowLiveblogNotifications =
isLiveBlog && !!pageId && !!headline;

const isImmersiveOrAnalysisWithMultipleAuthors =
(isAnalysis || isImmersive) && !!byline && isUndefined(soleContributor);

Expand Down Expand Up @@ -311,14 +308,11 @@ export const ArticleMetaApps = ({
/>
</Island>
)}
{shouldShowLiveblogNotifications && (
<Island priority="critical">
<LiveblogNotifications
displayName={headline}
id={pageId}
/>
</Island>
)}
<LiveblogNotifications
isLiveBlog={isLiveBlog}
headline={headline}
pageId={pageId}
/>
</MetaGridByline>
{isCommentable && (
<MetaGridCommentCount
Expand Down Expand Up @@ -379,3 +373,35 @@ export const ArticleMetaApps = ({
</div>
);
};

const LiveblogNotifications = (props: {
isLiveBlog: boolean;
headline: string | undefined;
pageId: string | undefined;
}) =>
props.isLiveBlog && !!props.pageId && !!props.headline ? (
<div
css={css`
margin-top: ${space[3]}px;
min-height: ${space[6]}px;

${from.phablet} {
display: inline-flex;
flex-direction: column;

button:first-of-type {
margin-right: ${space[5]}px;
}
}
`}
data-gu-name="liveblog-notifications"
>
<Island priority="critical">
<NotificationsToggle
id={props.pageId}
displayName={props.headline}
notificationType="content"
/>
</Island>
</div>
) : null;
117 changes: 0 additions & 117 deletions dotcom-rendering/src/components/LiveblogNotifications.island.tsx

This file was deleted.

15 changes: 15 additions & 0 deletions dotcom-rendering/src/components/NotificationsToggle.island.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ComponentProps } from 'react';
import { getNotificationsClient } from '../lib/bridgetApi';
import { NotificationsToggle as NotificationsToggleComponent } from './NotificationsToggle';

type Props = Omit<
ComponentProps<typeof NotificationsToggleComponent>,
'notificationsClient'
>;

export const NotificationsToggle = (props: Props) => (
<NotificationsToggleComponent
notificationsClient={getNotificationsClient()}
{...props}
/>
);
75 changes: 75 additions & 0 deletions dotcom-rendering/src/components/NotificationsToggle.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Topic } from '@guardian/bridget';
import type { Meta, StoryObj } from '@storybook/react-webpack5';
import { expect, fn, userEvent, within } from 'storybook/test';
import type { NotificationsClient } from '../lib/bridgetApi';
import { NotificationsToggle as NotificationsToggleComponent } from './NotificationsToggle';

const meta = {
component: NotificationsToggleComponent,
} satisfies Meta<typeof NotificationsToggleComponent>;

export default meta;

type Story = StoryObj<typeof meta>;

const mockNotificationsClient: NotificationsClient = (() => {
let following = false;

return {
follow: fn(() => {
following = true;
return Promise.resolve(true);
}),

unfollow: fn(() => {
following = false;
return Promise.resolve(true);
}),

isFollowing: fn(() => {
return Promise.resolve(following);
}),
};
})();

export const NotificationsToggle = {
args: {
displayName: 'A notification',
id: 'a-notification-id',
notificationType: 'content',
notificationsClient: mockNotificationsClient,
},
play: async ({ args, step, canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button');
const expectedTopic = new Topic({
displayName: args.displayName,
id: args.id,
type: args.notificationType,
});

await expect(button).toHaveTextContent('Notifications off');

await step('isFollowing is called', async () => {
await expect(
mockNotificationsClient.isFollowing,
).toHaveBeenCalledWith(expectedTopic);
});

await step('follow is called when button is clicked', async () => {
await userEvent.click(button);
await expect(mockNotificationsClient.follow).toHaveBeenCalledWith(
expectedTopic,
);
await expect(button).toHaveTextContent('Notifications on');
});

await step('unfollow is called when button is clicked', async () => {
await userEvent.click(button);
await expect(mockNotificationsClient.unfollow).toHaveBeenCalledWith(
expectedTopic,
);
await expect(button).toHaveTextContent('Notifications off');
});
},
} satisfies Story;
Loading
Loading