-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Works using focusedNodeId from focusStore and passing it down to useFocusNode hook
- Loading branch information
Daniele Lubrano
committed
Feb 9, 2022
1 parent
c0b0794
commit da852e3
Showing
4 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import React from 'react'; | ||
import '@testing-library/jest-dom'; | ||
import { render, act } from '@testing-library/react'; | ||
import { | ||
FocusRoot, | ||
FocusNode, | ||
useLeafFocusedNode, | ||
useSetFocus, | ||
useFocusStoreDangerously, | ||
} from '../index'; | ||
import { warning } from '../utils/warning'; | ||
|
||
describe('useLeafFocusedNode', () => { | ||
it('warns when there is no FocusRoot', () => { | ||
let focusNode; | ||
function TestComponent() { | ||
focusNode = useLeafFocusedNode(); | ||
|
||
return <div />; | ||
} | ||
|
||
render(<TestComponent />); | ||
|
||
expect(focusNode).toEqual(null); | ||
expect(warning).toHaveBeenCalledTimes(2); | ||
expect(warning.mock.calls[0][1]).toEqual('NO_FOCUS_PROVIDER_DETECTED'); | ||
}); | ||
|
||
it('returns the expected node', () => { | ||
let setFocus; | ||
let focusNode; | ||
let focusStore; | ||
|
||
function TestComponent() { | ||
setFocus = useSetFocus(); | ||
focusNode = useLeafFocusedNode(); | ||
focusStore = useFocusStoreDangerously(); | ||
|
||
return ( | ||
<> | ||
<FocusNode focusId="nodeA" data-testid="nodeA" /> | ||
<FocusNode focusId="nodeB" data-testid="nodeB" /> | ||
</> | ||
); | ||
} | ||
|
||
render( | ||
<FocusRoot> | ||
<TestComponent /> | ||
</FocusRoot> | ||
); | ||
|
||
expect(focusNode).toBe(focusStore.getState().nodes.nodeA); | ||
|
||
expect(focusNode).toEqual( | ||
expect.objectContaining({ | ||
focusId: 'nodeA', | ||
isFocused: true, | ||
isFocusedLeaf: true, | ||
}) | ||
); | ||
|
||
expect(focusStore.getState().nodes.nodeB).toEqual( | ||
expect.objectContaining({ | ||
focusId: 'nodeB', | ||
isFocused: false, | ||
isFocusedLeaf: false, | ||
}) | ||
); | ||
|
||
act(() => setFocus('nodeB')); | ||
|
||
expect(focusNode).toBe(focusStore.getState().nodes.nodeB); | ||
|
||
expect(focusStore.getState().nodes.nodeA).toEqual( | ||
expect.objectContaining({ | ||
focusId: 'nodeA', | ||
isFocused: false, | ||
isFocusedLeaf: false, | ||
}) | ||
); | ||
|
||
expect(focusNode).toEqual( | ||
expect.objectContaining({ | ||
focusId: 'nodeB', | ||
isFocused: true, | ||
isFocusedLeaf: true, | ||
}) | ||
); | ||
}); | ||
|
||
it('returns the root node if there are no focusable nodes (default focus state behavior)', () => { | ||
let focusNode; | ||
let focusStore; | ||
|
||
function TestComponent() { | ||
focusNode = useLeafFocusedNode(); | ||
focusStore = useFocusStoreDangerously(); | ||
|
||
return ( | ||
<> | ||
<div>No Focusable</div> | ||
</> | ||
); | ||
} | ||
|
||
render( | ||
<FocusRoot> | ||
<TestComponent /> | ||
</FocusRoot> | ||
); | ||
const focusRootNode = Object.values(focusStore.getState().nodes).find( | ||
(n) => n.isRoot | ||
); | ||
|
||
expect(focusNode).toEqual(focusRootNode); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { useContext, useState, useEffect, useRef } from 'react'; | ||
import FocusContext from '../focus-context'; | ||
import { warning } from '../utils/warning'; | ||
import { Node } from '../types'; | ||
import { useFocusNode } from '..'; | ||
|
||
export default function useFocusedNode(): Node | null { | ||
const contextValue = useContext(FocusContext.Context); | ||
const [focusNodeId, setFocusNodeId] = useState<string>(() => { | ||
if (!contextValue) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
warning( | ||
'A FocusProvider was not found in the tree. Did you forget to mount it?', | ||
'NO_FOCUS_PROVIDER_DETECTED' | ||
); | ||
} | ||
|
||
return ''; | ||
} | ||
|
||
const focusState = contextValue.store.getState(); | ||
return focusState.focusedNodeId; | ||
}); | ||
|
||
const focusNodeIdRef = useRef(focusNodeId); | ||
focusNodeIdRef.current = focusNodeId; | ||
|
||
function checkForSync() { | ||
if (!contextValue) { | ||
return; | ||
} | ||
|
||
const currentNodeId = contextValue.store.getState().focusedNodeId; | ||
if (currentNodeId !== focusNodeIdRef.current) { | ||
setFocusNodeId(currentNodeId); | ||
} | ||
} | ||
|
||
useEffect(checkForSync, [focusNodeId]); | ||
|
||
useEffect(() => { | ||
if (!contextValue) { | ||
return; | ||
} | ||
|
||
checkForSync(); | ||
const unsubscribe = contextValue.store.subscribe(checkForSync); | ||
|
||
return () => { | ||
unsubscribe(); | ||
}; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []); | ||
const focusNode = useFocusNode(focusNodeId); | ||
return focusNodeId ? focusNode : null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters