Skip to content
Merged
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
29 changes: 0 additions & 29 deletions special-pages/pages/new-tab/app/components/Icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -602,35 +602,6 @@ export function CloseSmallIcon(props) {
);
}

/**
* @param {import('preact').JSX.SVGAttributes<SVGSVGElement>} props
*/
export function NewBadgeIcon(props) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="42" height="16" viewBox="0 0 42 16" fill="none" {...props}>
<path
d="M0 3.99792C0 1.78879 1.79086 -0.0020752 4 -0.0020752H38C40.2091 -0.0020752 42 1.78879 42 3.99792V11.9979C42 14.2071 40.2091 15.9979 38 15.9979H4C1.79086 15.9979 0 14.2071 0 11.9979V3.99792Z"
fill="#F9BE1A"
/>
<path
d="M13.0913 9.1073H13.1812V3.94617H14.8032V12.0497H13.3999L9.64893 6.86707H9.55908V12.0497H7.93604V3.94617H9.35107L13.0913 9.1073Z"
fill="black"
fill-opacity="0.96"
/>
<path
d="M22.144 5.3446H18.4722V7.29871H21.936V8.60144H18.4722V10.6512H22.144V12.0497H16.7759V3.94617H22.144V5.3446Z"
fill="black"
fill-opacity="0.96"
/>
<path
d="M26.4663 9.73621H26.5562L28.0337 3.94617H29.4653L30.9702 9.73621H31.0601L32.312 3.94617H34.064L31.9136 12.0497H30.3247L28.7915 6.59167H28.7017L27.1851 12.0497H25.5854L23.4399 3.94617H25.2036L26.4663 9.73621Z"
fill="black"
fill-opacity="0.96"
/>
</svg>
);
}

/**
* @param {import('preact').JSX.SVGAttributes<SVGSVGElement>} props
*/
Expand Down
17 changes: 17 additions & 0 deletions special-pages/pages/new-tab/app/components/NewBadge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { h } from 'preact';
import styles from './NewBadge.module.css';

/**
* Badge component that displays text in a yellow rounded rectangle.
*
* @param {object} props
* @param {string} props.text - The text to display in the badge
* @param {import('preact').ComponentProps<'span'>} [props.rest] - Additional HTML attributes
*/
export function NewBadge({ text, ...rest }) {
return (
<span class={styles.badge} {...rest}>
{text?.toUpperCase() || 'NEW'}
</span>
);
}
16 changes: 16 additions & 0 deletions special-pages/pages/new-tab/app/components/NewBadge.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: fit-content;
height: 16px;
padding: 0 8px;
background-color: #F9BE1A;
border-radius: 4px;
font-family: var(--theme-font-family, system-ui);
font-weight: 700;
font-size: 11px;
color: var(--color-black-at-96);
letter-spacing: -0.015em;
flex-shrink: 0;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.tickPill {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 10px;
border-radius: 100px;
gap: 4px;
padding: 8px 8px;
border-radius: 1000px;
background-color: var(--color-white-at-3);
border: 1px solid var(--color-white-at-12);
height: 20px;
Expand Down Expand Up @@ -32,8 +32,8 @@

/* Light mode styles */
[data-theme="light"] .tickPill {
background-color: var(--color-black-at-4);
border: 1px solid var(--color-black-at-12);
background-color: var(--color-black-at-1);
border: 1px solid var(--color-black-at-9);
}

[data-theme="light"] .text {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
height: 16px;
display: inline-block;
vertical-align: middle;
margin-top: 2px;
}

.widgetExpander {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"note": "The heading indicating multiple cookie pop-ups were handled by the CPM"
},
"stats_protectionsReportInfo": {
"title": "Displays tracking attempts blocked in the last 7 days, and the number of cookie pop-ups blocked since you started using the browser. <span>You can reset these stats using the Fire Button.</span>",
"title": "Displays tracking attempts blocked in the last 7 days, and the number of cookie pop-ups blocked since you started using the browser.",
"note": "Text explaining how to reset the protections stats"
},
"stats_feedCountBlockedSingular": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
}

.block {
margin-top: 32px;
margin-top: 16px;
}

/* @todo legacyProtections: Remove once all platforms support the new UI */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useTypedTranslationWith } from '../../types.js';
import { useTypedTranslationWith, useMessaging } from '../../types.js';
import styles from '../../privacy-stats/components/PrivacyStats.module.css';
import { ShowHideButtonCircle } from '../../components/ShowHideButton.jsx';
import cn from 'classnames';
import { h } from 'preact';
import { InfoIcon, NewBadgeIcon } from '../../components/Icons.js';
import { InfoIcon } from '../../components/Icons.js';
import { NewBadge } from '../../components/NewBadge.js';
import { Tooltip } from '../../components/Tooltip/Tooltip.js';
import { useAnimatedCount } from '../utils/useAnimatedCount.js';
import { useRef, useEffect } from 'preact/hooks';

/**
* @import enStrings from "../strings.json"
Expand All @@ -29,6 +31,9 @@ export function ProtectionsHeading({
totalCookiePopUpsBlockedSignal,
}) {
const { t } = useTypedTranslationWith(/** @type {Strings} */ ({}));
const ntp = useMessaging();
const headingRef = useRef(/** @type {HTMLDivElement|null} */ (null));
const counterContainerRef = useRef(/** @type {HTMLDivElement|null} */ (null));
const totalTrackersBlocked = blockedCountSignal.value;
const totalCookiePopUpsBlockedValue = totalCookiePopUpsBlockedSignal.value;

Expand All @@ -38,9 +43,16 @@ export function ProtectionsHeading({
? Math.max(0, Math.floor(totalCookiePopUpsBlockedValue))
: 0;

// Animate both tracker count and cookie pop-ups count
const animatedTrackersBlocked = useAnimatedCount(totalTrackersBlocked);
const animatedCookiePopUpsBlocked = useAnimatedCount(totalCookiePopUpsBlocked);
// Animate both tracker count and cookie pop-ups count when counterContainer is in viewport
const animatedTrackersBlocked = useAnimatedCount(totalTrackersBlocked, counterContainerRef);
const animatedCookiePopUpsBlocked = useAnimatedCount(totalCookiePopUpsBlocked, counterContainerRef);

// Subscribe to scroll message
useEffect(() => {
return ntp.messaging.subscribe('protections_scroll', () => {
headingRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
}, [ntp]);

// Native does not tell the FE if cookie pop up protection is enabled but
// we can derive this from the value of `totalCookiePopUpsBlocked` in the
Expand All @@ -54,7 +66,7 @@ export function ProtectionsHeading({
animatedCookiePopUpsBlocked === 1 ? t('stats_totalCookiePopUpsBlockedSingular') : t('stats_totalCookiePopUpsBlockedPlural');

return (
<div class={styles.heading} data-testid="ProtectionsHeading">
<div class={styles.heading} data-testid="ProtectionsHeading" ref={headingRef}>
<div class={cn(styles.control, animatedTrackersBlocked === 0 && styles.noTrackers)}>
<span class={styles.headingIcon}>
<img src={'./icons/Shield-Check-Color-16.svg'} alt="Privacy Shield" />
Expand All @@ -79,7 +91,7 @@ export function ProtectionsHeading({
</span>
)}
</div>
<div class={styles.counterContainer}>
<div class={styles.counterContainer} ref={counterContainerRef}>
{/* Total Trackers Blocked */}
<div class={styles.counter}>
{animatedTrackersBlocked === 0 && <h3 class={styles.noRecentTitle}>{t('protections_noRecent')}</h3>}
Expand All @@ -99,9 +111,9 @@ export function ProtectionsHeading({
<h3 class={styles.title}>
{animatedCookiePopUpsBlocked} <span>{cookiePopUpsBlockedHeading}</span>
</h3>
{/* @todo `NewBadgeIcon` will be manually removed in
{/* @todo `NewBadge` will be manually removed in
a future iteration */}
<NewBadgeIcon />
<NewBadge text={t('protections_newBadge')} />
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,24 @@ export class ProtectionsPage {
await tooltipTrigger.press('Space');
await expect(tooltip).toBeVisible();
}

/**
* Triggers the scroll to protections heading via subscription message
*/
async scrollsToProtectionsHeading() {
const heading = this.context().getByTestId('ProtectionsHeading');
await expect(heading).toBeVisible();

// Scroll away from the element first
await this.page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

// Trigger the scroll message
await this.ntp.mocks.simulateSubscriptionMessage(named.subscription('protections_scroll'), {});

// Wait for smooth scroll animation to complete
await this.page.waitForTimeout(500);

// Verify the heading is now in viewport
await expect(heading).toBeInViewport();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,14 @@ test.describe('protections report', () => {
await protections.ready();
await protections.hasInfoTooltip();
});

test('scrolls to protections heading via subscription message', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo);
await ntp.reducedMotion();
await ntp.openPage({ additional: { 'protections.feed': 'activity' } });

const protections = new ProtectionsPage(ntp);
await protections.ready();
await protections.scrollsToProtectionsHeading();
});
});
4 changes: 4 additions & 0 deletions special-pages/pages/new-tab/app/protections/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@
"protections_noRecent": {
"title": "No recent tracking activity",
"note": "Placeholder text shown in the Details view when no tracking activity was blocked in the last 7 days. Keep concise if possible."
},
"protections_newBadge": {
"title": "NEW",
"note": "Text displayed in a badge to indicate a new feature or statistic."
}
}
Loading
Loading