Skip to content
Open
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
10 changes: 5 additions & 5 deletions cypress/e2e/specialFeatures/scriptSwitching/helpers/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MEDIA_ASSET_PAGE } from '../../../../../src/app/routes/utils/pageTypes';

const clickFirstArticleLink = () => {
cy.get('a[href*="articles"]').first().click();
const clickFirstLink = () => {
cy.get('a').first().click();
Comment on lines +3 to +4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I think the last commit/merge of latest has removed a bunch of changes: 1326d3e

};

const clickFirstMapLink = () => {
Expand All @@ -12,11 +12,11 @@ const clickFirstMapLink = () => {
.parentsUntil('[data-testid="topic-promos"]')
.first()
.within(() => {
cy.get('a').first().click();
clickFirstLink();
});
} else {
// If a MAP item isn't found on the home page, click the first promo item.
clickFirstArticleLink();
clickFirstLink();
}
});
};
Expand All @@ -41,7 +41,7 @@ export const clickPromoLinkOnHomePage = pageType => {
.first()
.within(() => {
// If it isn't a MAP page being tested, click the first promo item
clickFirstArticleLink();
clickFirstLink();
});
}
};
5 changes: 1 addition & 4 deletions src/app/components/CollapsibleNavigation/index.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const styles = {
height: `${pixelsToRem(20)}rem`,
backgroundColor: palette.GREY_10,
},
'&:nth-last-child(2)::after': {
'&:last-of-type::after': {
background: 'none',
},
}),
Expand Down Expand Up @@ -222,9 +222,6 @@ const styles = {
backgroundColor: palette.WHITE,
},
}),
collapsed: css({
display: 'none',
}),
};

export default styles;
49 changes: 23 additions & 26 deletions src/app/components/CollapsibleNavigation/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,70 +32,67 @@ describe('LanguageNavigation', () => {
render(<CollapsibleNavigation navigationSections={sections} />);

sections.forEach(section => {
const element = section.href
? screen.getByRole('link', { name: section.title })
: screen.getByRole('button', { name: section.title });
expect(element).toBeInTheDocument();
expect(screen.getByText(section.title)).toBeInTheDocument();
});
});

test('clicking a section toggles dropdown', () => {
render(<CollapsibleNavigation navigationSections={sections} />);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');

fireEvent.click(sectionTitle);

sections[0].links?.forEach(link => {
expect(screen.getAllByText(link.label)[0]).toBeInTheDocument();
expect(screen.getByText(link.label)).toBeInTheDocument();
});
});

test('clicking the same section again closes the dropdown', () => {
render(<CollapsibleNavigation navigationSections={sections} />);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');

fireEvent.click(sectionTitle);

expect(screen.getAllByText('Link 1')[0]).toBeVisible();
expect(screen.getAllByText('Link 2')[0]).toBeVisible();
expect(screen.getByText('Link 1')).toBeInTheDocument();
expect(screen.getByText('Link 2')).toBeInTheDocument();

fireEvent.click(sectionTitle);

expect(screen.getAllByText('Link 1')[0]).not.toBeVisible();
expect(screen.getAllByText('Link 2')[0]).not.toBeVisible();
expect(screen.queryByText('Link 1')).not.toBeInTheDocument();
expect(screen.queryByText('Link 2')).not.toBeInTheDocument();
});

test('clicking close button closes the dropdown', () => {
render(<CollapsibleNavigation navigationSections={sections} />);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');

fireEvent.click(sectionTitle);

expect(screen.getAllByText('Link 1')[0]).toBeInTheDocument();
expect(screen.getAllByText('Link 2')[0]).toBeInTheDocument();
expect(screen.getByText('Link 1')).toBeInTheDocument();
expect(screen.getByText('Link 2')).toBeInTheDocument();

const closeButton = screen.getByRole('button', {
name: 'Close Section 1 submenu',
});
fireEvent.click(closeButton);

expect(closeButton).not.toBeVisible();
expect(screen.queryAllByText('Link 1')[0]).not.toBeVisible();
expect(screen.queryAllByText('Link 2')[0]).not.toBeVisible();
expect(closeButton).not.toBeInTheDocument();
expect(screen.queryByText('Link 1')).not.toBeInTheDocument();
expect(screen.queryByText('Link 2')).not.toBeInTheDocument();
});

test('renders links correctly when section is active', () => {
render(<CollapsibleNavigation navigationSections={sections} />);

const sectionTitle = screen.getByRole('button', { name: 'Section 2' });
const sectionTitle = screen.getByText('Section 2');

fireEvent.click(sectionTitle);

sections[1].links?.forEach(link => {
expect(screen.getAllByText(link.label)[0]).toBeInTheDocument();
expect(screen.getByText(link.label)).toBeInTheDocument();
});
});

Expand All @@ -108,13 +105,13 @@ describe('LanguageNavigation', () => {

fireEvent.click(sectionLink);

expect(screen.queryAllByText('Link 1')[0]).not.toBeVisible();
expect(screen.queryByText('Link 1')).not.toBeInTheDocument();
});

test('applies lang attribute to links when provided', () => {
render(<CollapsibleNavigation navigationSections={sections} />);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');
fireEvent.click(sectionTitle);

const link1 = screen.getByRole('link', { name: 'Link 1' });
Expand Down Expand Up @@ -154,7 +151,7 @@ describe('LanguageNavigation', () => {
/>,
);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');
fireEvent.click(sectionTitle);

const link1 = screen.getByRole('link', {
Expand Down Expand Up @@ -187,7 +184,7 @@ describe('LanguageNavigation', () => {
<CollapsibleNavigation navigationSections={sectionsWithBothAttributes} />,
);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');
fireEvent.click(sectionTitle);

const link1 = screen.getByRole('link', {
Expand Down Expand Up @@ -216,7 +213,7 @@ describe('LanguageNavigation', () => {

render(<CollapsibleNavigation navigationSections={sectionsWithoutLang} />);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');
fireEvent.click(sectionTitle);

const link1 = screen.getByRole('link', { name: 'Link 1' });
Expand Down Expand Up @@ -247,7 +244,7 @@ describe('LanguageNavigation', () => {
/>,
);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');
fireEvent.click(sectionTitle);

const link1 = screen.getByRole('link', { name: 'Link 1' });
Expand Down Expand Up @@ -275,7 +272,7 @@ describe('LanguageNavigation', () => {

render(<CollapsibleNavigation navigationSections={sectionsNotTranslate} />);

const sectionTitle = screen.getByRole('button', { name: 'Section 1' });
const sectionTitle = screen.getByText('Section 1');
fireEvent.click(sectionTitle);

const link1 = screen.getByRole('link', { name: 'Link 1' });
Expand Down
9 changes: 3 additions & 6 deletions src/app/components/CollapsibleNavigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const CollapsibleNavigation = ({
<ul role="list" css={styles.navList} data-testid="collapsible-nav">
{navigationSections.map(section => {
const isActive = Boolean(openSection === section.id);
const shouldShowSubNav = isHydrated ? isActive : true;
const isLink = section.href;

const navigationLinkId = `nav-${section.id}`;
Expand All @@ -112,14 +113,10 @@ const CollapsibleNavigation = ({
</a>
</li>

{section.links && (
{section.links && shouldShowSubNav && (
<li
id={subNavigationId}
css={[
styles.subNav,
!isHydrated && styles.subNavNoJs,
!isActive && styles.collapsed,
]}
css={[styles.subNav, !isHydrated && styles.subNavNoJs]}
role="region"
aria-labelledby={subNavigationTitleId}
tabIndex={-1}
Expand Down
90 changes: 22 additions & 68 deletions src/app/components/PWAPromotionalBanner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ import useAndroidDetection from '#app/hooks/useAdroidDetection';
import useClickTracker from '#app/hooks/useClickTrackerHandler';
import useViewTracker from '#app/hooks/useViewTracker';
import useCustomEventTracker from '#app/hooks/useCustomEventTracker';
import VisuallyHiddenText from '#app/components/VisuallyHiddenText';
import useIsPWA from '#app/hooks/useIsPWA';

const PWA_PROMOTIONAL_BANNER_EXPERIMENT_NAME = 'newswb_ws_pwa_promo_prompt';
const PWA_BANNER_DISMISS_KEY = 'pwa_promotional_banner_dismissals';
const PWA_BANNER_LAST_DISMISS_KEY = 'pwa_promotional_banner_last_dismissed';
const PWA_BANNER_MAX_DISMISSALS = 3;
Expand Down Expand Up @@ -42,42 +39,25 @@ const isBannerVisible = () => {
return true;
};

type ExperimentControlProps = {
experimentVariant: string;
};

// Control group - fire the control group pixel
const PWAPromotionalBannerControl = ({
experimentVariant,
}: ExperimentControlProps) => {
const { isInstallable } = usePWAInstallPrompt({ deferPrompt: false });
const viewTracker = useViewTracker({
componentName: 'pwa-promotional-banner',
experimentName: PWA_PROMOTIONAL_BANNER_EXPERIMENT_NAME,
experimentVariant,
alwaysInView: true,
});

if (!isInstallable) {
return null;
}

return <VisuallyHiddenText {...viewTracker} />;
};

// Treatment group - actual PWA banner
const PWAPromotionalBannerTreatment = ({
experimentVariant,
}: ExperimentControlProps) => {
const PWAPromotionalBanner = () => {
const { promotionalBanner } = use(ServiceContext);
const { isLite, isAmp } = use(RequestContext);
const [isVisible, setIsVisible] = useState(() => isBannerVisible());

// EXPERIMENT: PWA Promotional Banner
const pwaPromoBannerExperimentName = 'newswb_ws_pwa_promo_prompt';
const pwaPromoBannerVariant = useOptimizelyVariation({
experimentName: pwaPromoBannerExperimentName,
experimentType: ExperimentType.SERVER_SIDE,
});

const isPwaPromoExperimentEnabled = pwaPromoBannerVariant === 'on';
const isAndroid = useAndroidDetection();

const optimizelyExperimentData = {
experimentName: PWA_PROMOTIONAL_BANNER_EXPERIMENT_NAME,
experimentVariant,
experimentName: pwaPromoBannerExperimentName,
experimentVariant: pwaPromoBannerVariant || 'off',
};

const viewTracker = useViewTracker({
componentName: 'pwa-promotional-banner',
...optimizelyExperimentData,
Expand Down Expand Up @@ -121,7 +101,6 @@ const PWAPromotionalBannerTreatment = ({
},
[onSecondaryClickTrack, handleBannerDismiss],
);

const { promptInstall, isInstallable } = usePWAInstallPrompt({
onAccepted: () => {
setIsVisible(false);
Expand All @@ -143,7 +122,15 @@ const PWAPromotionalBannerTreatment = ({
[onPrimaryClickTrack, promptInstall],
);

if (!isVisible || !isInstallable || !promotionalBanner) {
if (
isLite ||
isAmp ||
!isAndroid ||
!isPwaPromoExperimentEnabled ||
!isVisible ||
!isInstallable ||
!promotionalBanner
) {
return null;
}

Expand Down Expand Up @@ -173,37 +160,4 @@ const PWAPromotionalBannerTreatment = ({
</div>
);
};

const PWAPromotionalBanner = () => {
const { isLite, isAmp } = use(RequestContext);
const isPWA = useIsPWA();
const isAndroid = useAndroidDetection();

const pwaPromoBannerExperimentName = PWA_PROMOTIONAL_BANNER_EXPERIMENT_NAME;
const pwaPromoBannerVariant = useOptimizelyVariation({
experimentName: pwaPromoBannerExperimentName,
experimentType: ExperimentType.SERVER_SIDE,
});

if (isLite || isAmp || isPWA || !isAndroid) {
return null;
}

if (pwaPromoBannerVariant === 'control') {
return (
<PWAPromotionalBannerControl experimentVariant={pwaPromoBannerVariant} />
);
}

if (pwaPromoBannerVariant === 'on') {
return (
<PWAPromotionalBannerTreatment
experimentVariant={pwaPromoBannerVariant}
/>
);
}

return null;
};

export default PWAPromotionalBanner;
10 changes: 3 additions & 7 deletions src/app/hooks/usePWAInstallPrompt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@ interface UsePWAInstallPromptCallbacks {
onDismissed?: () => void;
onError?: (error: unknown) => void;
onPromptShown?: () => void;
deferPrompt?: boolean;
}

const usePWAInstallPrompt = ({
onAccepted,
onDismissed,
onError,
onPromptShown,
deferPrompt = true,
}: UsePWAInstallPromptCallbacks = {}) => {
const deferredPrompt = useRef<BeforeInstallPromptEvent | null>(null);
const [isInstallable, setIsInstallable] = useState(false);
Expand All @@ -32,10 +30,8 @@ const usePWAInstallPrompt = ({
return undefined;
}
const handleBeforeInstallPrompt = (event: Event) => {
if (deferPrompt) {
event?.preventDefault();
deferredPrompt.current = event as BeforeInstallPromptEvent;
}
if (typeof event.preventDefault === 'function') event.preventDefault();
deferredPrompt.current = event as BeforeInstallPromptEvent;
setIsInstallable(true);
};
window.addEventListener(
Expand All @@ -51,7 +47,7 @@ const usePWAInstallPrompt = ({
handleBeforeInstallPrompt as EventListener,
);
};
}, [deferPrompt, isPWA]);
}, [isPWA]);

const promptInstall = async () => {
if (!deferredPrompt.current) return;
Expand Down
Loading
Loading