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
137 changes: 137 additions & 0 deletions POC-GUEST-PREVIEW-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Temporary Guest My News POC

This POC implements a temporary signed-in preview experience for guest users to test the hypothesis that allowing non-registered users to build a temporary My News experience will increase BBC account registrations.

## Features Implemented

### 1. Enhanced Save Button for Guests
- **Location**: `src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/`
- **Features**:
- Hover tooltip on save button with info: "Save this article for later. It will appear in your My News page."
- Save/unsave functionality using localStorage
- Confirmation notification after saving with link to My News
- Visual feedback showing saved state

### 2. Temporary Article Storage
- **Location**: `src/app/hooks/useTemporarySavedArticles/`
- **Features**:
- Stores articles in browser localStorage
- 2-day expiry period
- Auto-cleanup on expiry
- Tracks save timestamp and article metadata

### 3. Temporary My News Page
- **Location**: `ws-nextjs-app/pages/[service]/my-news/MyNewsPage/MyNewsPageTemporary/`
- **Features**:
- Displays saved articles in a grid
- Prominent banner explaining temporary nature
- Shows countdown to expiry
- Prompts for registration/sign-in with call-to-action buttons
- Integrates with existing AccountActionButtons component

### 4. Integration Points
- **Article Pages**: Save button now uses `enableGuestPreview` prop to activate POC
- **My News Page**: Automatically shows temporary view when user has saved articles but isn't signed in
- **Smooth transition**: When user signs in, they see their temporary saves migrate to permanent storage

Comment on lines +32 to +36
## Usage

### Enable on Article Pages
The POC is enabled by default on article pages. The `SaveArticleButton` component accepts an `enableGuestPreview` prop:

```tsx
<SaveArticleButton
saveArticlePageData={extractSaveArticleProps(articlePageData)}
enableGuestPreview={true}
/>
```

### User Flow

1. **Article with Save Button**
- Guest user sees "Save for later" button
- Hovering shows tooltip explaining the feature

2. **Clicking Save**
- Article is saved to localStorage
- Confirmation notification appears with "View My News" link

3. **Visiting My News Page**
- Shows temporary My News page with:
- Banner: "This is a temporary page available for 2 days"
- Expiry countdown
- Sign in / Register buttons
- Grid of saved articles

4. **Registration/Sign-in**
- User clicks register or sign in
- Completes account creation
- Returns to My News with personalized experience
- (Future enhancement: migrate temporary saves to permanent account)

## Technical Details

### Data Storage
- **Key**: `bbc_temp_saved_articles`
- **Expiry Key**: `bbc_temp_saved_articles_expiry`
- **Format**: JSON array of article objects
- **Lifecycle**: 2 days from first save

### Article Data Structure
```typescript
{
id: string;
title: string;
link: string;
imageUrl?: string;
imageAlt?: string;
promoImage?: string;
type: string;
description: string;
savedAt: number;
}
```

### Components Reused
- `SaveButton` - Base save button component
- `CurationGrid` - Displays article grid
- `AccountActionButtons` - Sign in/register buttons
- `AccountSignInModal` - Modal for guest save (original flow)
- `Heading`, `Text`, `CallToActionLink` - UI primitives

## Files Created/Modified

### New Files
- `src/app/hooks/useTemporarySavedArticles/index.ts`
- `src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/index.tsx`
- `src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/SaveButtonTooltip.tsx`
- `src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/SaveArticleConfirmation.tsx`
- `ws-nextjs-app/pages/[service]/my-news/MyNewsPage/MyNewsPageTemporary/index.tsx`

### Modified Files
- `src/app/components/SaveArticleButton/index.tsx` - Added `enableGuestPreview` prop
- `ws-nextjs-app/pages/[service]/my-news/MyNewsPage/index.tsx` - Added conditional rendering for temporary view
- `src/app/pages/ArticlePage/ArticlePage.tsx` - Enabled guest preview on article pages

## Future Enhancements

1. **Migration on Sign-in**: Transfer temporary saves to permanent UAS storage when user creates account
2. **Analytics**: Track conversion rates from temporary saves to registrations
3. **Recommendations**: Show personalized recommendations based on temporary saves
4. **Persistence Warning**: Show warning before expiry (e.g., "1 hour left")
5. **Cross-device**: Sync temporary saves using anonymous token
6. **Topic/Place Following**: Extend to topics and places, not just articles

## Testing

This POC does not include test suites as requested. For manual testing:

1. Open any article page as a guest user
2. Hover over the save button to see tooltip
3. Click save and verify confirmation appears
4. Navigate to My News page
5. Verify temporary banner and articles display
6. Wait 2 days or manually clear localStorage to test expiry
7. Sign in to test transition (saves will not migrate in POC)

[copilot]
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { use, useEffect } from 'react';
import { ServiceContext } from '#contexts/ServiceContext';
import Text from '#app/components/Text';
import CallToActionLink from '#app/components/CallToActionLink';
import { Close } from '#app/components/icons';
import { css } from '@emotion/react';

const styles = {
overlay: css({
position: 'absolute',
Comment on lines +6 to +10
top: '100%',
left: '0',
marginTop: '0.75rem',
backgroundColor: '#fff',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '1rem',
minWidth: '280px',
maxWidth: '350px',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
zIndex: 1000,
animation: 'fadeIn 0.3s ease-out',
'@keyframes fadeIn': {
from: {
opacity: 0,
transform: 'translateY(-10px)',
},
to: {
opacity: 1,
transform: 'translateY(0)',
},
},
}),
header: css({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: '0.5rem',
}),
closeButton: css({
background: 'none',
border: 'none',
padding: '0.25rem',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
'&:hover': {
opacity: 0.7,
},
}),
content: css({
marginTop: '0.5rem',
}),
link: css({
marginTop: '0.75rem',
display: 'inline-block',
}),
};

interface SaveArticleConfirmationProps {
onClose: () => void;
}

const SaveArticleConfirmation = ({ onClose }: SaveArticleConfirmationProps) => {
const { service } = use(ServiceContext);

const confirmationText = 'Article saved to My News';
const linkText = 'View My News';

const myNewsPath = `/${service}/my-news`;

useEffect(() => {
const timer = setTimeout(() => {
onClose();
}, 5000);

return () => clearTimeout(timer);
}, [onClose]);

return (
<div css={styles.overlay} role="alert">
<div css={styles.header}>
<Text size="pica" fontVariant="sansBold">
{confirmationText}
</Text>
<button
type="button"
onClick={onClose}
css={styles.closeButton}
aria-label="Close"
>
<Close width="16" height="16" />
</button>
</div>
<div css={styles.content}>
<CallToActionLink
url={myNewsPath}
css={styles.link}
eventTrackingData={{
componentName: 'save-article-confirmation-link',
}}
>
<CallToActionLink.Text shouldUnderlineOnHoverFocus>
{linkText}
</CallToActionLink.Text>
</CallToActionLink>
</div>
</div>
);
};

export default SaveArticleConfirmation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { css } from '@emotion/react';
import Text from '#app/components/Text';

interface SaveButtonTooltipProps {
isSaved?: boolean;
}

const styles = {
tooltip: css({
position: 'absolute',
bottom: '100%',
left: '50%',
transform: 'translateX(-50%)',
marginBottom: '0.5rem',
padding: '0.75rem 1rem',
backgroundColor: '#fff',
color: '#222',
border: '1px solid #ccc',
borderRadius: '4px',
whiteSpace: 'normal',
maxWidth: '280px',
width: 'max-content',
minWidth: '200px',
zIndex: 1000,
boxShadow: '0 2px 12px rgba(0,0,0,0.15)',
textAlign: 'center',
'&::after': {
content: '""',
position: 'absolute',
top: '100%',
left: '50%',
marginLeft: '-8px',
borderWidth: '8px',
borderStyle: 'solid',
borderColor: '#fff transparent transparent transparent',
},
'&::before': {
content: '""',
position: 'absolute',
top: '100%',
left: '50%',
marginLeft: '-9px',
borderWidth: '9px',
borderStyle: 'solid',
borderColor: '#ccc transparent transparent transparent',
zIndex: -1,
},
}),
};

const SaveButtonTooltip = ({ isSaved = false }: SaveButtonTooltipProps) => {
const unsavedText =
'Save this article for later. It will appear in your My News page.';
const savedText = 'This article is saved temporarily in your My News page.';

const tooltipText = isSaved ? savedText : unsavedText;
Comment on lines +51 to +56

return (
<div css={styles.tooltip} role="tooltip">
<Text size="brevier" fontVariant="sansRegular" css={{ color: '#222' }}>
{tooltipText}
</Text>
</div>
);
};

export default SaveButtonTooltip;
Loading
Loading