Skip to content

Commit dcbed22

Browse files
committed
[fragment-scroll] Stop focusing the first focusable host descendant
1 parent b42b301 commit dcbed22

3 files changed

Lines changed: 32 additions & 21 deletions

File tree

packages/next/src/client/components/layout-router.tsx

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,12 @@ function getHashFragmentDomNode(hashFragment: string) {
139139
document.getElementsByName(hashFragment)[0]
140140
)
141141
}
142-
interface ScrollAndFocusHandlerProps {
142+
interface ScrollAndMaybeFocusHandlerProps {
143143
focusAndScrollRef: FocusAndScrollRef
144144
children: React.ReactNode
145145
segmentPath: FlightSegmentPath
146146
}
147-
class InnerScrollAndFocusHandlerOld extends React.Component<ScrollAndFocusHandlerProps> {
147+
class InnerScrollAndFocusHandlerOld extends React.Component<ScrollAndMaybeFocusHandlerProps> {
148148
handlePotentialScroll = () => {
149149
// Handle scroll and focus, it's only applied once.
150150
const { focusAndScrollRef, segmentPath } = this.props
@@ -265,7 +265,11 @@ class InnerScrollAndFocusHandlerOld extends React.Component<ScrollAndFocusHandle
265265
}
266266
}
267267

268-
function InnerScrollAndFocusHandlerNew(props: ScrollAndFocusHandlerProps) {
268+
/**
269+
* Fork of InnerScrollAndFocusHandlerOld using Fragment refs for scrolling.
270+
* No longer focuses the first host descendant.
271+
*/
272+
function InnerScrollHandlerNew(props: ScrollAndMaybeFocusHandlerProps) {
269273
const childrenRef = React.useRef<FragmentInstance>(null)
270274

271275
useLayoutEffect(
@@ -309,6 +313,18 @@ function InnerScrollAndFocusHandlerNew(props: ScrollAndFocusHandlerProps) {
309313
focusAndScrollRef.hashFragment = null
310314
focusAndScrollRef.segmentPaths = []
311315

316+
const activeElement = document.activeElement
317+
if (
318+
activeElement !== null &&
319+
'blur' in activeElement &&
320+
typeof activeElement.blur === 'function'
321+
) {
322+
// Matching hard navigations so that the focus cursor is before the new segment
323+
// There might be more content before the new segment but that's better
324+
// than staying on the link that triggered the navigation.
325+
activeElement.blur()
326+
}
327+
312328
disableSmoothScrollDuringRouteTransition(
313329
() => {
314330
// In case of hash scroll, we only need to scroll the element into view
@@ -348,11 +364,6 @@ function InnerScrollAndFocusHandlerNew(props: ScrollAndFocusHandlerProps) {
348364

349365
// Mutate after scrolling so that it can be read by `disableSmoothScrollDuringRouteTransition`
350366
focusAndScrollRef.onlyHashChange = false
351-
352-
// Set focus on the element but don't scroll since we already did that.
353-
// The focus might have targetted a deep element outside of the instances
354-
// top edge.
355-
instance.focus({ preventScroll: true })
356367
}
357368
},
358369
// Used to run on every commit. We may be able to be smarter about this
@@ -363,11 +374,11 @@ function InnerScrollAndFocusHandlerNew(props: ScrollAndFocusHandlerProps) {
363374
return <Fragment ref={childrenRef}>{props.children}</Fragment>
364375
}
365376

366-
const InnerScrollAndFocusHandler = enableNewScrollHandler
367-
? InnerScrollAndFocusHandlerNew
377+
const InnerScrollAndMaybeFocusHandler = enableNewScrollHandler
378+
? InnerScrollHandlerNew
368379
: InnerScrollAndFocusHandlerOld
369380

370-
function ScrollAndFocusHandler({
381+
function ScrollAndMaybeFocusHandler({
371382
segmentPath,
372383
children,
373384
}: {
@@ -380,12 +391,12 @@ function ScrollAndFocusHandler({
380391
}
381392

382393
return (
383-
<InnerScrollAndFocusHandler
394+
<InnerScrollAndMaybeFocusHandler
384395
segmentPath={segmentPath}
385396
focusAndScrollRef={context.focusAndScrollRef}
386397
>
387398
{children}
388-
</InnerScrollAndFocusHandler>
399+
</InnerScrollAndMaybeFocusHandler>
389400
)
390401
}
391402

@@ -768,7 +779,7 @@ export default function OuterLayoutRouter({
768779
<TemplateContext.Provider
769780
key={stateKey}
770781
value={
771-
<ScrollAndFocusHandler segmentPath={segmentPath}>
782+
<ScrollAndMaybeFocusHandler segmentPath={segmentPath}>
772783
<ErrorBoundary
773784
errorComponent={error}
774785
errorStyles={errorStyles}
@@ -809,7 +820,7 @@ export default function OuterLayoutRouter({
809820
</LoadingBoundary>
810821
</ErrorBoundary>
811822
{segmentViewStateNode}
812-
</ScrollAndFocusHandler>
823+
</ScrollAndMaybeFocusHandler>
813824
}
814825
>
815826
{templateStyles}

test/development/browser-logs/browser-logs.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { retry } from 'next-test-utils'
88
const bundlerName = process.env.IS_TURBOPACK_TEST ? 'Turbopack' : 'Webpack'
99
const enableNewScrollHandler =
1010
process.env.__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER === 'true'
11-
const innerScrollAndFocusHandlerName = enableNewScrollHandler
12-
? 'InnerScrollAndFocusHandlerNew'
11+
const innerScrollAndMaybeFocusHandlerName = enableNewScrollHandler
12+
? 'InnerScrollHandlerNew'
1313
: 'InnerScrollAndFocusHandlerOld'
1414

1515
function setupLogCapture() {
@@ -336,8 +336,8 @@ describe(`Terminal Logging (${bundlerName})`, () => {
336336
337337
...
338338
<RenderFromTemplateContext>
339-
<ScrollAndFocusHandler segmentPath={[...]}>
340-
<${innerScrollAndFocusHandlerName} segmentPath={[...]} focusAndScrollRef={{apply:false, ...}}>
339+
<ScrollAndMaybeFocusHandler segmentPath={[...]}>
340+
<${innerScrollAndMaybeFocusHandlerName} segmentPath={[...]} focusAndScrollRef={{apply:false, ...}}>
341341
<ErrorBoundary errorComponent={undefined} errorStyles={undefined} errorScripts={undefined}>
342342
<LoadingBoundary name="hydration-..." loading={null}>
343343
<HTTPAccessFallbackBoundary notFound={undefined} forbidden={undefined} unauthorized={undefined}>

test/lib/next-test-utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,12 +1487,12 @@ const nextjsClientComponentNames = [
14871487
'HTTPAccessFallbackErrorBoundary',
14881488
'InnerLayoutRouter',
14891489
'InnerScrollAndFocusHandlerOld',
1490-
'InnerScrollAndFocusHandlerNew',
1490+
'InnerScrollHandlerNew',
14911491
'RedirectBoundary',
14921492
'RedirectErrorBoundary',
14931493
'RenderFromTemplateContext',
14941494
'Root',
1495-
'ScrollAndFocusHandler',
1495+
'ScrollAndMaybeFocusHandler',
14961496
'SegmentViewNode',
14971497
'SegmentTrieNode',
14981498
// These are added due to user actions e.g. loading.js -> LoadingBoundary

0 commit comments

Comments
 (0)