Skip to content

Commit

Permalink
Merge a1c4bd7 into 2f8e52c
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesplease committed Jun 17, 2021
2 parents 2f8e52c + a1c4bd7 commit 23e0436
Show file tree
Hide file tree
Showing 18 changed files with 2,416 additions and 14 deletions.
1 change: 1 addition & 0 deletions jest-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@testing-library/jest-dom';
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ module.exports = {
collectCoverageFrom: ['src/**/*.{ts,tsx}', '!**/node_modules/**'],
coverageDirectory: 'coverage',
testURL: 'http://localhost/',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['./jest-setup.js'],
};
16 changes: 10 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"types": "types/index.d.ts",
"scripts": {
"test": "NODE_ENV=test jest",
"test:watch": "jest --watch",
"test:watch": "NODE_ENV=test jest --watchAll",
"clean": "rimraf ./dist ./es ./tmp ./lib ./types",
"prepublishOnly": "npm run build",
"postpublish": "npm run clean",
Expand Down Expand Up @@ -53,23 +53,27 @@
},
"devDependencies": {
"@babel/cli": "^7.10.5",
"@babel/core": "^7.11.1",
"@babel/core": "^7.14.6",
"@babel/plugin-external-helpers": "^7.10.4",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4",
"@babel/preset-env": "^7.11.0",
"@babel/preset-react": "^7.10.4",
"@babel/preset-typescript": "^7.10.4",
"@types/jest": "26.0.9",
"@types/node": "^14.0.27",
"@testing-library/react": "11.2.7",
"@testing-library/jest-dom": "5.14.1",
"@types/jest": "^26.0.23",
"@types/node": "^15.12.2",
"@types/react": "^17.0.11",
"babel-eslint": "^10.1.0",
"babel-jest": "26.3.0",
"babel-jest": "27.0.2",
"babel-loader": "^8.0.6",
"coveralls": "3.1.0",
"cross-env": "^7.0.2",
"eslint": "^7.6.0",
"jest": "26.4.0",
"jest": "27.0.4",
"prettier": "^2.0.5",
"react": "17.0.2",
"react-dom": "17.0.2",
"rimraf": "^3.0.2",
"typescript": "^4.3.2"
}
Expand Down
5 changes: 3 additions & 2 deletions src/focus-node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -459,15 +459,16 @@ export function FocusNode(
const isLeaf =
nodeRef.current && nodeRef.current.children.length === 0;
const isDisabled = nodeRef.current && nodeRef.current.disabled;

if (!isLeaf || isDisabled) {
return;
}

const focusState = staticDefinitions.providerValue.store.getState();

if (
!focusState._hasPointerEventsEnabled ||
!nodeExistsInTree.current
!nodeExistsInTree.current ||
focusState.interactionMode !== 'pointer'
) {
return;
}
Expand Down
224 changes: 224 additions & 0 deletions src/focus-root.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import React from 'react';
import '@testing-library/jest-dom';
import { render, fireEvent, screen } from '@testing-library/react';
import { FocusRoot, FocusNode, useFocusStoreDangerously } from './index';

describe('<FocusRoot/>', () => {
describe('wrapping', () => {
it('does not wrap by default', () => {
let focusStore;

function TestComponent() {
focusStore = useFocusStoreDangerously();

return (
<>
<FocusNode focusId="nodeA" data-testid="nodeA">
A
</FocusNode>
<FocusNode focusId="nodeB" data-testid="nodeB">
B
</FocusNode>
</>
);
}

render(
<FocusRoot>
<TestComponent />
</FocusRoot>
);

const nodeAEl = screen.getByTestId('nodeA');
expect(nodeAEl).toHaveClass('isFocused');
expect(nodeAEl).toHaveClass('isFocusedLeaf');

const nodeBEl = screen.getByTestId('nodeB');
expect(nodeBEl).not.toHaveClass('isFocused');
expect(nodeBEl).not.toHaveClass('isFocusedLeaf');

const focusState = focusStore.getState();
expect(focusState.focusedNodeId).toEqual('nodeA');
expect(focusState.focusHierarchy).toEqual(['root', 'nodeA']);
expect(focusState.activeNodeId).toEqual(null);
expect(Object.values(focusState.nodes)).toHaveLength(3);

fireEvent.keyDown(window, {
code: 'ArrowRight',
key: 'ArrowRight',
});

expect(focusStore.getState().focusedNodeId).toEqual('nodeB');

fireEvent.keyDown(window, {
code: 'ArrowRight',
key: 'ArrowRight',
});

expect(focusStore.getState().focusedNodeId).toEqual('nodeB');
});

it('supports wrapping', () => {
let focusStore;

function TestComponent() {
focusStore = useFocusStoreDangerously();

return (
<>
<FocusNode focusId="nodeA" data-testid="nodeA">
A
</FocusNode>
<FocusNode focusId="nodeB" data-testid="nodeB">
B
</FocusNode>
</>
);
}

render(
<FocusRoot wrapping>
<TestComponent />
</FocusRoot>
);

const nodeAEl = screen.getByTestId('nodeA');
expect(nodeAEl).toHaveClass('isFocused');
expect(nodeAEl).toHaveClass('isFocusedLeaf');

const nodeBEl = screen.getByTestId('nodeB');
expect(nodeBEl).not.toHaveClass('isFocused');
expect(nodeBEl).not.toHaveClass('isFocusedLeaf');

const focusState = focusStore.getState();
expect(focusState.focusedNodeId).toEqual('nodeA');
expect(focusState.focusHierarchy).toEqual(['root', 'nodeA']);
expect(focusState.activeNodeId).toEqual(null);
expect(Object.values(focusState.nodes)).toHaveLength(3);

fireEvent.keyDown(window, {
code: 'ArrowRight',
key: 'ArrowRight',
});

expect(focusStore.getState().focusedNodeId).toEqual('nodeB');

fireEvent.keyDown(window, {
code: 'ArrowRight',
key: 'ArrowRight',
});

expect(focusStore.getState().focusedNodeId).toEqual('nodeA');
});
});

describe('orientation', () => {
it('can be configured to be vertical', () => {
let focusStore;

function TestComponent() {
focusStore = useFocusStoreDangerously();

return (
<>
<FocusNode focusId="nodeA" data-testid="nodeA">
A
</FocusNode>
<FocusNode focusId="nodeB" data-testid="nodeB">
B
</FocusNode>
</>
);
}

render(
<FocusRoot orientation="vertical">
<TestComponent />
</FocusRoot>
);

const nodeAEl = screen.getByTestId('nodeA');
expect(nodeAEl).toHaveClass('isFocused');
expect(nodeAEl).toHaveClass('isFocusedLeaf');

const nodeBEl = screen.getByTestId('nodeB');
expect(nodeBEl).not.toHaveClass('isFocused');
expect(nodeBEl).not.toHaveClass('isFocusedLeaf');

const focusState = focusStore.getState();
expect(focusState.focusedNodeId).toEqual('nodeA');
expect(focusState.focusHierarchy).toEqual(['root', 'nodeA']);
expect(focusState.activeNodeId).toEqual(null);
expect(Object.values(focusState.nodes)).toHaveLength(3);

fireEvent.keyDown(window, {
code: 'ArrowDown',
key: 'ArrowDown',
});

expect(focusStore.getState().focusedNodeId).toEqual('nodeB');

fireEvent.keyDown(window, {
code: 'ArrowDown',
key: 'ArrowDown',
});

expect(focusStore.getState().focusedNodeId).toEqual('nodeB');
});
});

describe('orientation + wrapping', () => {
it('functions together', () => {
let focusStore;

function TestComponent() {
focusStore = useFocusStoreDangerously();

return (
<>
<FocusNode focusId="nodeA" data-testid="nodeA">
A
</FocusNode>
<FocusNode focusId="nodeB" data-testid="nodeB">
B
</FocusNode>
</>
);
}

render(
<FocusRoot orientation="vertical" wrapping>
<TestComponent />
</FocusRoot>
);

const nodeAEl = screen.getByTestId('nodeA');
expect(nodeAEl).toHaveClass('isFocused');
expect(nodeAEl).toHaveClass('isFocusedLeaf');

const nodeBEl = screen.getByTestId('nodeB');
expect(nodeBEl).not.toHaveClass('isFocused');
expect(nodeBEl).not.toHaveClass('isFocusedLeaf');

const focusState = focusStore.getState();
expect(focusState.focusedNodeId).toEqual('nodeA');
expect(focusState.focusHierarchy).toEqual(['root', 'nodeA']);
expect(focusState.activeNodeId).toEqual(null);
expect(Object.values(focusState.nodes)).toHaveLength(3);

fireEvent.keyDown(window, {
code: 'ArrowDown',
key: 'ArrowDown',
});

expect(focusStore.getState().focusedNodeId).toEqual('nodeB');

fireEvent.keyDown(window, {
code: 'ArrowDown',
key: 'ArrowDown',
});

expect(focusStore.getState().focusedNodeId).toEqual('nodeA');
});
});
});
20 changes: 20 additions & 0 deletions src/focus-store.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import createFocusStore from './focus-store';

describe('createFocusStore', () => {
it('returns an object with the right shape', () => {
const focusStore = createFocusStore();

expect(focusStore).toBeTruthy();

expect(typeof focusStore.subscribe).toEqual('function');
expect(typeof focusStore.getState).toEqual('function');
expect(typeof focusStore.createNodes).toEqual('function');
expect(typeof focusStore.deleteNode).toEqual('function');
expect(typeof focusStore.setFocus).toEqual('function');
expect(typeof focusStore.updateNode).toEqual('function');
expect(typeof focusStore.handleArrow).toEqual('function');
expect(typeof focusStore.handleSelect).toEqual('function');
expect(typeof focusStore.configurePointerEvents).toEqual('function');
expect(typeof focusStore.destroy).toEqual('function');
});
});
2 changes: 2 additions & 0 deletions src/focus-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ export default function createFocusStore({
}
}

return;
} else if (currentNode.disabled) {
return;
} else if (currentNode.isExiting) {
if (process.env.NODE_ENV !== 'production') {
Expand Down
Loading

0 comments on commit 23e0436

Please sign in to comment.