From 50e2d956c4711efc6a8ad925eb0d3d32123755dc Mon Sep 17 00:00:00 2001 From: dognose24 Date: Wed, 9 Sep 2026 01:26:25 +0800 Subject: [PATCH 1/3] Premium Analytics: keep the onboarding welcome modal open on a click outside A click on the backdrop dismissed the welcome modal, and since the onboarding opens once per reader that stray click lost the tour for good. Base UI's disablePointerDismissal keeps the modal up; the close button and Escape still dismiss it. Fixes UNI-750. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01E6EnRKyZ6hzCb2pm7Y4tXN --- .../changelog/fix-pa-onboarding-modal-outside-click | 4 ++++ .../__tests__/onboarding-welcome-modal.test.tsx | 11 +++++++++++ .../onboarding-welcome-modal.tsx | 11 ++++++----- .../stories/onboarding-welcome-modal.stories.tsx | 2 +- .../routes/dashboard/hooks/use-onboarding.ts | 2 +- .../changelog/fix-pa-onboarding-modal-outside-click | 4 ++++ .../changelog/fix-pa-onboarding-modal-outside-click | 4 ++++ 7 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 projects/packages/premium-analytics/changelog/fix-pa-onboarding-modal-outside-click create mode 100644 projects/plugins/jetpack/changelog/fix-pa-onboarding-modal-outside-click create mode 100644 projects/plugins/premium-analytics/changelog/fix-pa-onboarding-modal-outside-click diff --git a/projects/packages/premium-analytics/changelog/fix-pa-onboarding-modal-outside-click b/projects/packages/premium-analytics/changelog/fix-pa-onboarding-modal-outside-click new file mode 100644 index 000000000000..4e7737918875 --- /dev/null +++ b/projects/packages/premium-analytics/changelog/fix-pa-onboarding-modal-outside-click @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Onboarding: keep the welcome modal open on a click outside it, so the tour can still be started. diff --git a/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/__tests__/onboarding-welcome-modal.test.tsx b/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/__tests__/onboarding-welcome-modal.test.tsx index 76d491be6de8..e6792515732a 100644 --- a/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/__tests__/onboarding-welcome-modal.test.tsx +++ b/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/__tests__/onboarding-welcome-modal.test.tsx @@ -57,4 +57,15 @@ describe( 'OnboardingWelcomeModal', () => { expect( onDismiss ).toHaveBeenCalledTimes( 1 ); expect( onDismiss ).toHaveBeenCalledWith( 'escape' ); } ); + + it( 'stays open on a click outside, without counting a dismissal', async () => { + const { onDismiss } = renderModal(); + + await userEvent.click( document.body ); + + expect( + screen.getByRole( 'dialog', { name: 'Welcome to the new Traffic page' } ) + ).toBeInTheDocument(); + expect( onDismiss ).not.toHaveBeenCalled(); + } ); } ); diff --git a/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/onboarding-welcome-modal.tsx b/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/onboarding-welcome-modal.tsx index 9c0e9e7ab14f..9c3809af94cc 100644 --- a/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/onboarding-welcome-modal.tsx +++ b/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/onboarding-welcome-modal.tsx @@ -4,14 +4,13 @@ import { useCallback } from 'react'; import { WidgetGridAnimation } from '../widget-grid-animation'; import styles from './onboarding-welcome-modal.module.scss'; -/** How the reader closed the modal without starting: the close button, Escape or a click outside. */ -export type OnboardingDismissReason = 'close' | 'escape' | 'outside' | 'other'; +/** How the reader closed the modal without starting: the close button or Escape. */ +export type OnboardingDismissReason = 'close' | 'escape' | 'other'; -// Base UI names the cause on `onOpenChange`; these are the three a reader can produce. +// Base UI names the cause on `onOpenChange`; these are the two a reader can produce. const DISMISS_REASONS: Record< string, OnboardingDismissReason > = { 'close-press': 'close', 'escape-key': 'escape', - 'outside-press': 'outside', }; type OpenChangeDetails = { @@ -49,8 +48,10 @@ export function OnboardingWelcomeModal( { [ onDismiss ] ); + // A click on the backdrop must not end the journey: the modal opens once per + // reader, so a stray click would lose the tour for good. return ( - + { /* The stage lives in the scroll region so the copy and the button diff --git a/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/stories/onboarding-welcome-modal.stories.tsx b/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/stories/onboarding-welcome-modal.stories.tsx index 9159582647e0..b40205ebf739 100644 --- a/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/stories/onboarding-welcome-modal.stories.tsx +++ b/projects/packages/premium-analytics/packages/ui/src/onboarding-welcome-modal/stories/onboarding-welcome-modal.stories.tsx @@ -17,7 +17,7 @@ const meta: Meta< typeof OnboardingWelcomeModal > = { 'the tour.\n\n' + 'The consumer owns the open state. `onStart` fires when the reader ' + 'presses Take a quick tour; `onDismiss` when they close the dialog any other ' + - 'way, naming which (the close button, Escape, a click outside). The ' + + 'way, naming which (the close button or Escape; a click outside is ignored). The ' + 'onboarding hook decides what each one means for the journey.\n\n' + 'On viewports too short for the animation, the copy and the tour button ' + 'take the room instead; on the ones in between, the content scrolls ' + diff --git a/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts b/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts index 0c19d50240c7..ca2b822edbb4 100644 --- a/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts +++ b/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts @@ -13,7 +13,7 @@ import { useTrackEvent } from './use-track-event'; export type OnboardingPhase = 'closed' | 'modal' | 'tour'; /** How the reader closed the journey without finishing it. */ -export type OnboardingDismissReason = 'close' | 'escape' | 'outside' | 'other'; +export type OnboardingDismissReason = 'close' | 'escape' | 'other'; export type OnboardingOptions = { /** Whether the reader is on the surface the journey introduces; nothing opens until then. */ diff --git a/projects/plugins/jetpack/changelog/fix-pa-onboarding-modal-outside-click b/projects/plugins/jetpack/changelog/fix-pa-onboarding-modal-outside-click new file mode 100644 index 000000000000..c1d3a58b58b9 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-pa-onboarding-modal-outside-click @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +Premium Analytics: keep the onboarding welcome modal open on a click outside it, so the tour can still be started. diff --git a/projects/plugins/premium-analytics/changelog/fix-pa-onboarding-modal-outside-click b/projects/plugins/premium-analytics/changelog/fix-pa-onboarding-modal-outside-click new file mode 100644 index 000000000000..4e7737918875 --- /dev/null +++ b/projects/plugins/premium-analytics/changelog/fix-pa-onboarding-modal-outside-click @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Onboarding: keep the welcome modal open on a click outside it, so the tour can still be started. From b28ccc3cffb0a714a5fe1a19ef4ab9a9c3963de9 Mon Sep 17 00:00:00 2001 From: dognose24 Date: Wed, 9 Sep 2026 08:57:54 +0800 Subject: [PATCH 2/3] Premium Analytics: reuse the ui package's onboarding dismiss reason type Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01E6EnRKyZ6hzCb2pm7Y4tXN --- .../routes/dashboard/hooks/use-onboarding.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts b/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts index ca2b822edbb4..2ad91f243532 100644 --- a/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts +++ b/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts @@ -1,3 +1,6 @@ +/** + * External dependencies + */ /** * WordPress dependencies */ @@ -9,11 +12,11 @@ import { store as preferencesStore } from '@wordpress/preferences'; */ import { DASHBOARD_ONBOARDING_KEY, DASHBOARD_PREFERENCES_SCOPE } from './constants'; import { useTrackEvent } from './use-track-event'; +import type { OnboardingDismissReason } from '@jetpack-premium-analytics/ui'; export type OnboardingPhase = 'closed' | 'modal' | 'tour'; -/** How the reader closed the journey without finishing it. */ -export type OnboardingDismissReason = 'close' | 'escape' | 'other'; +export type { OnboardingDismissReason }; export type OnboardingOptions = { /** Whether the reader is on the surface the journey introduces; nothing opens until then. */ From 93a09355c644afb0d9da65275c45fd0d1b7fd573 Mon Sep 17 00:00:00 2001 From: dognose24 Date: Wed, 9 Sep 2026 08:58:40 +0800 Subject: [PATCH 3/3] Premium Analytics: drop the empty import header left by the autofix Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01E6EnRKyZ6hzCb2pm7Y4tXN --- .../premium-analytics/routes/dashboard/hooks/use-onboarding.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts b/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts index 2ad91f243532..474c51f5e947 100644 --- a/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts +++ b/projects/packages/premium-analytics/routes/dashboard/hooks/use-onboarding.ts @@ -1,6 +1,3 @@ -/** - * External dependencies - */ /** * WordPress dependencies */