Hide whats-new modal on public demo

This commit is contained in:
rcourtman 2026-04-11 21:54:43 +01:00
parent df2fccb011
commit 8e6fe4142f
4 changed files with 38 additions and 2 deletions

View file

@ -1130,6 +1130,10 @@ product copy, or external links back into the shared shell. Internal product
navigation from that shell should still route through canonical shared helpers
such as `frontend-modern/src/routing/resourceLinks.ts` rather than freezing raw
`/recovery?...` route strings into the modal itself.
That state owner now also owns public-demo suppression: the modal must stay
closed until `sessionPresentationPolicyResolved()` is true and must fail closed
when `presentationPolicyIsDemoMode()` resolves true, so the public demo does
not front-load product migration onboarding ahead of the actual surface.
Canonical customer disclosures inside those shared shells now route through
`frontend-modern/src/utils/docsLinks.ts`, so settings and what's-new privacy
links resolve to shipped `/docs/...` assets instead of hard-coded GitHub

View file

@ -1134,6 +1134,8 @@ describe('shared primitive guardrails', () => {
expect(whatsNewModalStateSource).toContain('createLocalStorageBooleanSignal');
expect(whatsNewModalStateSource).toContain('createSignal');
expect(whatsNewModalStateSource).toContain('STORAGE_KEYS.WHATS_NEW_NAV_V2_SHOWN');
expect(whatsNewModalStateSource).toContain('sessionPresentationPolicyResolved');
expect(whatsNewModalStateSource).toContain('presentationPolicyIsDemoMode');
expect(whatsNewModalStateSource).toContain('handleClose');
expect(whatsNewModalModelSource).toContain('WHATS_NEW_FEATURE_CARDS');

View file

@ -1,4 +1,4 @@
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { cleanup, fireEvent, render, screen, waitFor } from '@solidjs/testing-library';
import { WhatsNewModal } from '@/components/shared/WhatsNewModal';
import whatsNewModalSource from '@/components/shared/WhatsNewModal.tsx?raw';
@ -6,9 +6,19 @@ import whatsNewModalModelSource from '@/components/shared/whatsNewModalModel.ts?
import whatsNewModalStateSource from '@/components/shared/useWhatsNewModalState.ts?raw';
import { STORAGE_KEYS } from '@/utils/localStorage';
const presentationPolicyIsDemoModeMock = vi.hoisted(() => vi.fn(() => false));
const sessionPresentationPolicyResolvedMock = vi.hoisted(() => vi.fn(() => true));
vi.mock('@/stores/sessionPresentationPolicy', () => ({
presentationPolicyIsDemoMode: presentationPolicyIsDemoModeMock,
sessionPresentationPolicyResolved: sessionPresentationPolicyResolvedMock,
}));
describe('WhatsNewModal', () => {
beforeEach(() => {
localStorage.clear();
presentationPolicyIsDemoModeMock.mockReturnValue(false);
sessionPresentationPolicyResolvedMock.mockReturnValue(true);
});
afterEach(() => {
@ -31,6 +41,8 @@ describe('WhatsNewModal', () => {
expect(whatsNewModalStateSource).toContain('createLocalStorageBooleanSignal');
expect(whatsNewModalStateSource).toContain('createSignal');
expect(whatsNewModalStateSource).toContain('STORAGE_KEYS.WHATS_NEW_NAV_V2_SHOWN');
expect(whatsNewModalStateSource).toContain('sessionPresentationPolicyResolved');
expect(whatsNewModalStateSource).toContain('presentationPolicyIsDemoMode');
expect(whatsNewModalStateSource).toContain('handleClose');
expect(whatsNewModalModelSource).toContain('WHATS_NEW_FEATURE_CARDS');
@ -56,6 +68,16 @@ describe('WhatsNewModal', () => {
expect(screen.getByText('Welcome to the New Navigation!')).toBeInTheDocument();
});
it('stays hidden for public demo sessions', async () => {
presentationPolicyIsDemoModeMock.mockReturnValue(true);
render(() => <WhatsNewModal />);
await waitFor(() => {
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
});
it('closes on backdrop click and records the modal as seen by default', async () => {
render(() => <WhatsNewModal />);

View file

@ -1,5 +1,9 @@
import { createSignal } from 'solid-js';
import { createLocalStorageBooleanSignal, STORAGE_KEYS } from '@/utils/localStorage';
import {
presentationPolicyIsDemoMode,
sessionPresentationPolicyResolved,
} from '@/stores/sessionPresentationPolicy';
export function useWhatsNewModalState() {
const [hasSeen, setHasSeen] = createLocalStorageBooleanSignal(
@ -9,7 +13,11 @@ export function useWhatsNewModalState() {
const [dontShowAgain, setDontShowAgain] = createSignal(true);
const [dismissedForSession, setDismissedForSession] = createSignal(false);
const isOpen = () => !hasSeen() && !dismissedForSession();
const isOpen = () =>
sessionPresentationPolicyResolved() &&
!presentationPolicyIsDemoMode() &&
!hasSeen() &&
!dismissedForSession();
const handleClose = () => {
if (dontShowAgain()) {