@@ -42,6 +42,7 @@ export function OnboardingTour() {
4242 const router = useRouter ( )
4343 const [ run , setRun ] = useState ( getInitialRun )
4444 const [ stepIndex , setStepIndex ] = useState ( getInitialStepIndex )
45+ const [ targetMissing , setTargetMissing ] = useState ( false )
4546 const prevPathnameRef = useRef ( pathname )
4647 const originPathRef = useRef < string | null > ( null )
4748 const tourActive = run && pathname !== UNAUTHORISED_PATH
@@ -78,6 +79,30 @@ export function OnboardingTour() {
7879 queueMicrotask ( ( ) => setRun ( true ) )
7980 } , [ ] )
8081
82+ // When the tour is resumed on a page that doesn't contain the current
83+ // step's target (e.g. the page is reloaded with a mid-tour step index in
84+ // local storage), react-joyride renders its grey overlay but never renders
85+ // the tooltip - leaving the user with a grey screen and no close button.
86+ // Detect that case so we can let them click the overlay to close the tour.
87+ useEffect ( ( ) => {
88+ setTargetMissing ( false )
89+
90+ if ( ! tourActive ) return
91+
92+ const currentStep = steps [ stepIndex ]
93+ const target = currentStep ?. target
94+ if ( typeof target !== 'string' ) return
95+
96+ // Give react-joyride's own target polling (targetWaitTimeout) a chance to
97+ // find the element first, so we don't fire during normal navigation when
98+ // the target appears a moment after the route changes.
99+ const timeout = setTimeout ( ( ) => {
100+ setTargetMissing ( ! document . querySelector ( target ) )
101+ } , 1200 )
102+
103+ return ( ) => clearTimeout ( timeout )
104+ } , [ tourActive , steps , stepIndex , pathname ] )
105+
81106 useEffect ( ( ) => {
82107 window . addEventListener ( RESTART_ONBOARDING_TOUR_EVENT , restartTour )
83108 return ( ) =>
@@ -168,5 +193,26 @@ export function OnboardingTour() {
168193 } ,
169194 } )
170195
171- return Tour
196+ return (
197+ < >
198+ { Tour }
199+ { tourActive && targetMissing && (
200+ < div
201+ role = "button"
202+ aria-label = "Close tour"
203+ tabIndex = { 0 }
204+ onClick = { finishTour }
205+ onKeyDown = { ( event ) => {
206+ if ( event . key === 'Enter' || event . key === ' ' ) finishTour ( )
207+ } }
208+ style = { {
209+ position : 'fixed' ,
210+ inset : 0 ,
211+ zIndex : 10000 ,
212+ cursor : 'pointer' ,
213+ } }
214+ />
215+ ) }
216+ </ >
217+ )
172218}
0 commit comments