box-ui-elements-test
Version:
Box UI Elements
1,323 lines • 55 kB
JavaScript
import React from 'react';
import { Set } from 'immutable';
import sinon from 'sinon';
import isEqual from 'lodash/isEqual';
import makeSelectable from '../makeSelectable';
import shiftSelect from '../shiftSelect';
const sandbox = sinon.sandbox.create();
jest.mock('../shiftSelect');
jest.useFakeTimers();
describe('components/table/makeSelectable', () => {
const Table = (props = {}) => <table {...props} />;
const SelectableTable = makeSelectable(Table);
const data = ['a', 'b', 'c', 'd', 'e'];
const getWrapper = (props = {}) =>
shallow(<SelectableTable onSelect={sandbox.stub()} data={data} selectedItems={[]} enableHotkeys {...props} />);
afterEach(() => {
jest.clearAllTimers();
jest.clearAllMocks();
sandbox.verifyAndRestore();
});
describe('componentDidMount()', () => {
test('should add keypress listener', () => {
document.addEventListener = jest.fn();
const instance = getWrapper().instance();
expect(document.addEventListener).toBeCalledWith('keypress', instance.handleKeyboardSearch);
});
});
describe('componentDidUpdate()', () => {
test('should call onFocus handler when focused index changes', () => {
const onFocus = jest.fn();
const wrapper = getWrapper({
onFocus,
});
wrapper.setState({ focusedIndex: 3 });
expect(onFocus).toBeCalledWith(3);
});
});
describe('componentWillUnmount()', () => {
test('should remove keypress listener', () => {
document.removeEventListener = jest.fn();
const wrapper = getWrapper();
const instance = wrapper.instance();
wrapper.unmount();
expect(document.removeEventListener).toBeCalledWith('keypress', instance.handleKeyboardSearch);
});
});
describe('onSelect()', () => {
test('should set previousIndex to state.focusedIndex, set state.focusedIndex, and call onSelect', () => {
const wrapper = getWrapper({
onSelect: sandbox.mock().withArgs(['c']),
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.onSelect(new Set(['c']), 2);
expect(instance.previousIndex).toEqual(1);
expect(wrapper.state('focusedIndex')).toEqual(2);
});
test('should call onSelect with Immutable Set when selectedItems is given as a set', () => {
const wrapper = getWrapper({
selectedItems: new Set(['a']),
onSelect: items => {
expect(items.equals(new Set(['c']))).toBe(true);
},
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.onSelect(new Set(['c']), 2);
});
});
describe('getProcessedProps()', () => {
test('should return selectedItems as Immutable object when given as plain JS', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
const instance = wrapper.instance();
const processedProps = instance.getProcessedProps();
expect(processedProps.selectedItems.equals(new Set(['a']))).toBe(true);
});
});
describe('selectToggle()', () => {
test('should remove item from selection when it is selected', () => {
const wrapper = getWrapper({
selectedItems: ['a', 'b'],
});
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(['a']))).toBe(true);
expect(focusedIndex).toEqual(1);
};
instance.selectToggle(1);
});
test('should add item to selection when it is not selected', () => {
const wrapper = getWrapper({
selectedItems: [],
});
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(['b']))).toBe(true);
expect(focusedIndex).toEqual(1);
};
instance.selectToggle(1);
});
});
describe('selectRange()', () => {
afterEach(() => {
shiftSelect.mockReset();
});
test('should call shiftSelect with correct args', () => {
const selectedItems = ['a', 'b', 'c'];
const focusedIndex = 1;
const rowIndex = 2;
const anchorIndex = 3;
// expected computed value
const selectedRows = new Set([0, 1, 2]);
shiftSelect.mockImplementation(() => new Set([1, 2, 3]));
const wrapper = getWrapper({
selectedItems,
});
const instance = wrapper.instance();
instance.previousIndex = focusedIndex;
instance.anchorIndex = anchorIndex;
instance.onSelect = newSelectedItems => {
// newSelectedItems should be the item-mapped equivalent of
// [1, 2, 3] returned from the call to shiftSelect
expect(newSelectedItems.equals(new Set(['b', 'c', 'd']))).toBe(true);
};
instance.selectRange(rowIndex);
expect(shiftSelect).toHaveBeenCalledWith(selectedRows, focusedIndex, rowIndex, anchorIndex);
});
test('should not change selection if clicking on same row', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
instance.previousIndex = 1;
instance.selectRange(1);
expect(shiftSelect).not.toHaveBeenCalled();
});
});
describe('selectOne()', () => {
test('should not change selection when clicking on the only already-selected row', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
const instance = wrapper.instance();
instance.onSelect = sandbox.mock().never();
instance.selectOne(0);
});
test('should set selection to contain only the target item', () => {
const wrapper = getWrapper({
selectedItems: ['a', 'b'],
});
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(['c']))).toBe(true);
expect(focusedIndex).toEqual(2);
};
instance.selectOne(2);
});
});
describe('handleRowClick()', () => {
test('should call selectToggle() when meta key pressed', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const index = 1;
instance.selectToggle = sandbox.mock().withArgs(index);
instance.handleRowClick({ metaKey: true }, index);
});
test('should call selectToggle() when ctrl key pressed', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const index = 1;
instance.selectToggle = sandbox.mock().withArgs(index);
instance.handleRowClick({ ctrlKey: true }, index);
});
test('should call selectRange() when shift key pressed', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const index = 1;
instance.selectRange = sandbox.mock().withArgs(index);
instance.handleRowClick({ shiftKey: true }, index);
});
test('should call selectOne() when no modifier key pressed', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const index = 1;
instance.selectOne = sandbox.mock().withArgs(index);
instance.handleRowClick({}, index);
});
});
describe('handleCheckboxClick()', () => {
test('should call selectRange() when shift key pressed', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const index = 1;
instance.selectRange = sandbox.mock().withArgs(index);
instance.handleCheckboxClick({ nativeEvent: { shiftKey: true } }, index);
});
test('should call selectToggle() when no modifier key pressed', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const index = 1;
instance.selectToggle = sandbox.mock().withArgs(index);
instance.handleCheckboxClick({ nativeEvent: {} }, index);
});
});
describe('handleRowFocus()', () => {
test('should call onSelect() with correct args', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(['a']))).toBe(true);
expect(focusedIndex).toEqual(2);
};
instance.handleRowFocus({}, 2);
});
});
describe('handleShiftKeyDown()', () => {
test('should be no-op when target is the boundary and already selected', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
onSelect: sandbox.mock().never(),
});
wrapper.setState({ focusedIndex: 0 });
wrapper.instance().handleShiftKeyDown(0, 0);
});
test('should select target when it is not already selected and source is selected', () => {
const wrapper = getWrapper({
selectedItems: ['b'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(['a', 'b']))).toBe(true);
expect(focusedIndex).toEqual(0);
};
instance.handleShiftKeyDown(0, 0);
});
test('should deselect source when both source and target are selected', () => {
const wrapper = getWrapper({
selectedItems: ['a', 'b'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(['b']))).toBe(true);
expect(focusedIndex).toEqual(1);
};
instance.handleShiftKeyDown(1, 4);
});
test('should select source when target is selected but not source', () => {
const wrapper = getWrapper({
selectedItems: ['b'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(['a', 'b']))).toBe(true);
expect(focusedIndex).toEqual(1);
};
instance.handleShiftKeyDown(1, 0);
});
test('should select both source and target when neither are selected', () => {
const wrapper = getWrapper({
selectedItems: [],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(['a', 'b']))).toBe(true);
expect(focusedIndex).toEqual(1);
};
instance.handleShiftKeyDown(1, 4);
});
});
describe('isContiguousSelection()', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const { isContiguousSelection } = instance;
test('returns true if source is less than target and source - 1 is selected', () => {
expect(isContiguousSelection(Set([0]), 1, 2)).toEqual(true);
});
test('returns false if source is less than target and source - 1 is not selected', () => {
expect(isContiguousSelection(Set(), 1, 2)).toEqual(false);
});
test('returns true if source is greater than target and source + 1 is selected', () => {
expect(isContiguousSelection(Set([2]), 1, 0)).toEqual(true);
});
test('returns false if source is greater than target and source + 1 is not selected', () => {
expect(isContiguousSelection(Set(), 1, 2)).toEqual(false);
});
test('returns false if source and target are equal', () => {
expect(isContiguousSelection(Set([0, 2]), 1, 1)).toEqual(false);
});
});
describe('handleShiftKeyDownForGrid()', () => {
afterEach(() => {
shiftSelect.mockReset();
});
test('should select target when it is not already selected', () => {
const selectedItems = [];
const focusedIndex = 1;
const targetIndex = 0;
const anchorIndex = 1;
const wrapper = getWrapper({
selectedItems,
});
wrapper.setState({ focusedIndex });
const instance = wrapper.instance();
shiftSelect.mockImplementation(() => new Set([0]));
instance.onSelect = (funcSelectedItems, funcFocusedIndex) => {
expect(funcSelectedItems.equals(new Set(['a']))).toBe(true);
expect(funcFocusedIndex).toEqual(0);
};
instance.handleShiftKeyDownForGrid(targetIndex);
expect(shiftSelect).toHaveBeenCalledWith(Set(selectedItems), focusedIndex, targetIndex, anchorIndex);
});
test('should deselect source when both source and target are selected', () => {
const selectedItems = ['a', 'b'];
const focusedIndex = 0;
const targetIndex = 1;
const anchorIndex = 0;
const wrapper = getWrapper({
selectedItems,
});
wrapper.setState({ focusedIndex });
const instance = wrapper.instance();
shiftSelect.mockImplementation(() => new Set([1]));
instance.onSelect = (funcSelectedItems, funcFocusedIndex) => {
expect(funcSelectedItems.equals(new Set(['b']))).toBe(true);
expect(funcFocusedIndex).toEqual(1);
};
instance.handleShiftKeyDownForGrid(targetIndex);
expect(shiftSelect).toHaveBeenCalledWith(Set([0, 1]), focusedIndex, targetIndex, anchorIndex);
});
test('should select source when target is selected but not source', () => {
const selectedItems = ['b'];
const focusedIndex = 0;
const targetIndex = 1;
const anchorIndex = 0;
const wrapper = getWrapper({
selectedItems,
});
wrapper.setState({ focusedIndex });
const instance = wrapper.instance();
shiftSelect.mockImplementation(() => new Set([0, 1]));
instance.onSelect = (funcSelectedItems, funcFocusedIndex) => {
expect(funcSelectedItems.equals(new Set(['a', 'b']))).toBe(true);
expect(funcFocusedIndex).toEqual(1);
};
instance.handleShiftKeyDownForGrid(targetIndex);
expect(shiftSelect).toHaveBeenCalledWith(Set([1]), focusedIndex, targetIndex, anchorIndex);
});
test('should select source and target and set anchor when both are unselected', () => {
const selectedItems = [];
const focusedIndex = 2;
const targetIndex = 3;
const anchorIndex = 2;
const wrapper = getWrapper({
selectedItems,
});
wrapper.setState({ focusedIndex });
const instance = wrapper.instance();
shiftSelect.mockImplementation(() => new Set([2, 3]));
instance.onSelect = (funcSelectedItems, funcFocusedIndex) => {
expect(funcSelectedItems.equals(new Set(['c', 'd']))).toBe(true);
expect(funcFocusedIndex).toEqual(targetIndex);
};
instance.handleShiftKeyDownForGrid(targetIndex);
expect(shiftSelect).toHaveBeenCalledWith(Set([]), focusedIndex, targetIndex, anchorIndex);
});
test('should select source and target and not set anchor when both are unselected and it is a continuation', () => {
const selectedItems = ['a', 'b'];
const focusedIndex = 2;
const targetIndex = 3;
const anchorIndex = 0;
const wrapper = getWrapper({
selectedItems,
});
wrapper.setState({ focusedIndex });
const instance = wrapper.instance();
shiftSelect.mockImplementation(() => new Set([0, 1, 2, 3]));
instance.onSelect = (funcSelectedItems, funcFocusedIndex) => {
expect(funcSelectedItems.equals(new Set(['a', 'b', 'c', 'd']))).toBe(true);
expect(funcFocusedIndex).toEqual(targetIndex);
};
instance.handleShiftKeyDownForGrid(targetIndex);
expect(shiftSelect).toHaveBeenCalledWith(Set([0, 1]), focusedIndex, targetIndex, anchorIndex);
});
test('should set targetIndex to 0 when it is below 0', () => {
const selectedItems = ['b'];
const focusedIndex = 1;
const targetIndex = -1;
const anchorIndex = 0;
const wrapper = getWrapper({
selectedItems,
});
wrapper.setState({ focusedIndex });
const instance = wrapper.instance();
shiftSelect.mockImplementation(() => new Set([0, 1]));
instance.handleShiftKeyDownForGrid(targetIndex);
expect(shiftSelect).toHaveBeenCalledWith(Set([1]), focusedIndex, 0, anchorIndex);
});
test('should set targetIndex to max index when it is greater than the max index', () => {
const selectedItems = ['b'];
const focusedIndex = 1;
const targetIndex = data.length;
const anchorIndex = 0;
const wrapper = getWrapper({
selectedItems,
});
wrapper.setState({ focusedIndex });
const instance = wrapper.instance();
shiftSelect.mockImplementation(() => new Set([0, 1]));
instance.handleShiftKeyDownForGrid(targetIndex);
expect(shiftSelect).toHaveBeenCalledWith(Set([1]), focusedIndex, data.length - 1, anchorIndex);
});
});
describe('clearFocus()', () => {
test('should clear focus', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
wrapper.setState({ focusedIndex: 1 });
instance.clearFocus();
expect(wrapper.state('focusedIndex')).toBeUndefined();
});
});
describe('blur detection', () => {
test('should not set timer when table does not have focus', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
instance.clearFocus = jest.fn();
wrapper.setState({ focusedIndex: undefined });
instance.handleTableBlur();
jest.runAllTimers();
expect(instance.blurTimerID).toBeNull();
expect(instance.clearFocus).toBeCalledTimes(0);
});
test('should clear focus after timeout expires', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
instance.clearFocus = jest.fn();
wrapper.setState({ focusedIndex: 1 });
instance.handleTableBlur();
jest.runAllTimers();
expect(instance.clearFocus).toBeCalledTimes(1);
});
test('should not clear focus if focus is regained before timeout expires', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
instance.clearFocus = jest.fn();
wrapper.setState({ focusedIndex: 1 });
instance.handleTableBlur();
instance.handleTableFocus(); // regain focus
jest.runAllTimers();
expect(instance.clearFocus).toBeCalledTimes(0);
});
});
describe('keyboard shortcuts', () => {
test('should set and return this.hotkeys when this.hotkeys is null', () => {
const instance = getWrapper({}).instance();
// should be initially null
instance.hotkeys = null;
const shortcuts = instance.getHotkeyConfigs();
// should not be null anymore
expect(instance.hotkeys).not.toBeNull();
expect(shortcuts).toEqual(instance.hotkeys);
});
test('should use correct description and hotkey type for all shortcuts', () => {
const hotkeyType = 'item selection';
const instance = getWrapper({
hotkeyType,
}).instance();
const shortcuts = instance.getHotkeyConfigs();
shortcuts.forEach(shortcut => {
expect(shortcut.description).toBeTruthy();
expect(shortcut.type).toEqual(hotkeyType);
});
});
describe('meta+a / ctrl+a', () => {
const hotKey = ['meta+a', 'ctrl+a'];
test('should call event.preventDefault() and select all items', () => {
const wrapper = getWrapper({
selectedItems: [],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set(data))).toBe(true);
expect(focusedIndex).toEqual(1);
};
const shortcut = instance.getHotkeyConfigs().find(h => isEqual(h.get('key'), hotKey));
shortcut.handler({ preventDefault: sandbox.mock() });
});
});
describe('esc', () => {
const hotKey = 'esc';
test('should set selection to empty', () => {
const wrapper = getWrapper({
selectedItems: ['a', 'b', 'c'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.onSelect = (selectedItems, focusedIndex) => {
expect(selectedItems.equals(new Set([]))).toBe(true);
expect(focusedIndex).toEqual(1);
};
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
});
describe('shift+x', () => {
const hotKey = 'shift+x';
test('should be no-op when focusedIndex is undefined', () => {
const wrapper = getWrapper({
onSelect: sandbox.mock().never(),
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should call selectToggle on focused item', () => {
const wrapper = getWrapper({
focusedItem: 'b',
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.selectToggle = sandbox.mock().withArgs(1);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
});
describe('ListView specific', () => {
describe('down', () => {
const hotKey = 'down';
test('should set focus to first row when no currently focused item', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
test('should call event.preventDefault() and set focus to next item', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.mock() });
expect(wrapper.state('focusedIndex')).toEqual(1);
});
test('should not focus on an index higher than the highest index in the table', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 4 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(4);
});
});
describe('up', () => {
const hotKey = 'up';
test('should call event.preventDefault() and call onSelect with new focused item', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.mock() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
test('should not focus on an index lower than 0', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
});
describe('shift+down', () => {
const hotKey = 'shift+down';
test('should be no-op when focusedIndex is undefined', () => {
const wrapper = getWrapper({
onSelect: sandbox.mock().never(),
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should call handleShiftKeyDown() with the index of the next item in the table', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.handleShiftKeyDown = sandbox.mock().withArgs(1, data.length - 1);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should not call handleShiftKeyDown() with an index greater than the highest index', () => {
const wrapper = getWrapper({
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 4 });
const instance = wrapper.instance();
instance.handleShiftKeyDown = sandbox.mock().withArgs(data.length - 1, data.length - 1);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
});
describe('shift+up', () => {
const hotKey = 'shift+up';
test('should be no-op when focusedIndex is undefined', () => {
const wrapper = getWrapper({
onSelect: sandbox.mock().never(),
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should call handleShiftKeyDown() with index of the next item in the table', () => {
const wrapper = getWrapper({
focusedItem: 'b',
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.handleShiftKeyDown = sandbox.mock().withArgs(0, 0);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should not call handleShiftKeyDown() with an index lower than 0', () => {
const wrapper = getWrapper({
focusedItem: 'a',
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.handleShiftKeyDown = sandbox.mock().withArgs(0, 0);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
});
});
describe('GridView specific', () => {
const gridColumnCount = 3;
describe('right', () => {
const hotKey = 'right';
test('should set focus to first row when no currently focused item', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
test('should not set focus to first row if target has role of slider', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ target: { role: 'slider' } });
expect(wrapper.state('focusedIndex')).toEqual(undefined);
});
test('should call event.preventDefault() and set focus to next item', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.mock() });
expect(wrapper.state('focusedIndex')).toEqual(1);
});
test('should not focus on an index higher than the highest index in the table', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 4 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(4);
});
});
describe('left', () => {
const hotKey = 'left';
test('should not set focus to first row if target has role of slider', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ target: { role: 'slider' } });
expect(wrapper.state('focusedIndex')).toEqual(undefined);
});
test('should call event.preventDefault() and call onSelect with new focused item', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.mock() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
test('should call event.preventDefault() and set focus to previous item', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.mock() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
test('should not focus on an index lower than 0', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
});
describe('down', () => {
const hotKey = 'down';
test('should set focus to first row when no currently focused item', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
test('should not set focus to first row if target has role of slider', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ target: { role: 'slider' } });
expect(wrapper.state('focusedIndex')).toEqual(undefined);
});
test('should call event.preventDefault() and set focus to next row item', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.mock() });
expect(wrapper.state('focusedIndex')).toEqual(gridColumnCount);
});
test('should not focus on an index higher than the highest index in the table', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 4 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(4);
});
});
describe('up', () => {
const hotKey = 'up';
test('should not set focus to first row if target has role of slider', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ target: { role: 'slider' } });
expect(wrapper.state('focusedIndex')).toEqual(undefined);
});
test('should call event.preventDefault() and call onSelect with new focused item', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.mock() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
test('should call event.preventDefault() and set focus to previous row item', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: gridColumnCount });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.mock() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
test('should not focus on an index lower than 0', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler({ preventDefault: sandbox.stub() });
expect(wrapper.state('focusedIndex')).toEqual(0);
});
});
describe('shift+right', () => {
const hotKey = 'shift+right';
test('should be no-op when focusedIndex is undefined', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
onSelect: sandbox.mock().never(),
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should call handleShiftKeyDownForGrid() with the index of the next item in the table', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.handleShiftKeyDownForGrid = sandbox.mock().withArgs(1);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should not call handleShiftKeyDownForGrid() with an index greater than the highest index', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 4 });
const instance = wrapper.instance();
instance.handleShiftKeyDownForGrid = sandbox.mock().withArgs(data.length - 1);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
});
describe('shift+left', () => {
const hotKey = 'shift+left';
test('should be no-op when focusedIndex is undefined', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
onSelect: sandbox.mock().never(),
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should call handleShiftKeyDownForGrid() with index of the next item in the table', () => {
const wrapper = getWrapper({
focusedItem: 'b',
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.handleShiftKeyDownForGrid = sandbox.mock().withArgs(0);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should not call handleShiftKeyDownForGrid() with an index lower than 0', () => {
const wrapper = getWrapper({
focusedItem: 'a',
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.handleShiftKeyDownForGrid = sandbox.mock().withArgs(0);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
});
describe('shift+down', () => {
const hotKey = 'shift+down';
test('should be no-op when focusedIndex is undefined', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
onSelect: sandbox.mock().never(),
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should call handleShiftKeyDownForGrid() with the index of the next row item in the table', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.handleShiftKeyDownForGrid = sandbox.mock().withArgs(gridColumnCount);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should not call handleShiftKeyDownForGrid() with an index greater than the highest index', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 4 });
const instance = wrapper.instance();
instance.handleShiftKeyDownForGrid = sandbox.mock().withArgs(data.length - 1);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
});
describe('shift+up', () => {
const hotKey = 'shift+up';
test('should be no-op when focusedIndex is undefined', () => {
const wrapper = getWrapper({
gridColumnCount,
isGridView: true,
onSelect: sandbox.mock().never(),
});
wrapper.setState({ focusedIndex: undefined });
const instance = wrapper.instance();
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should call handleShiftKeyDownForGrid() with index of the next item in the table', () => {
const wrapper = getWrapper({
focusedItem: 'b',
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 1 });
const instance = wrapper.instance();
instance.handleShiftKeyDownForGrid = sandbox.mock().withArgs(0);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
test('should not call handleShiftKeyDownForGrid() with an index lower than 0', () => {
const wrapper = getWrapper({
focusedItem: 'a',
gridColumnCount,
isGridView: true,
selectedItems: ['a'],
});
wrapper.setState({ focusedIndex: 0 });
const instance = wrapper.instance();
instance.handleShiftKeyDownForGrid = sandbox.mock().withArgs(0);
const shortcut = instance.getHotkeyConfigs().find(h => h.get('key') === hotKey);
shortcut.handler();
});
});
});
});
describe('handleKeyboardSearch()', () => {
const searchStrings = ['abc', 'bcd', 'cde', 'def', 'efg'];
const target = document.createElement('div');
const getWrapperWithSearchStrings = () => getWrapper({ searchStrings });
test('should set index correctly to matching string', () => {
const wrapper = getWrapperWithSearchStrings();
const instance = wrapper.instance();
instance.searchString = 'd';
instance.handleKeyboardSearch({ target, key: 'e' });
expect(wrapper.state('focusedIndex')).toEqual(3); // should focus on "def"
});
test('should reset searchString after 1 second', () => {
const wrapper = getWrapperWithSearchStrings();
const instance = wrapper.instance();
// start typing "c"
instance.handleKeyboardSearch({ target, key: 'c' });
jest.advanceTimersByTime(1001);
// type "d"
instance.handleKeyboardSearch({ target, key: 'd' });
// should match "def" rather than "cde" due to timeout
expect(wrapper.state('focusedIndex')).toEqual(3);
});
test('should not change focused index when no string match', () => {
const wrapper = getWrapperWithSearchStrings();
wrapper.setState({ focusedIndex: 3 });
const instance = wrapper.instance();
instance.handleKeyboardSearch({ target, key: 'z' });
expect(wrapper.state('focusedIndex')).toEqual(3); // should not change
});
test('should be noop when event target is contenteditable', () => {
const wrapper = getWrapperWithSearchStrings();
wrapper.setState({ focusedIndex: 3 });
const instance = wrapper.instance();
instance.handleKeyboardSearch({
target: { hasAttribute: () => true },
key: 'a',
});
expect(wrapper.state('focusedIndex')).toEqual(3); // should not change
});
[
{
nodeName: 'INPUT',
},
{
nodeName: 'TEXTAREA',
},
].forEach(({ nodeName }) => {
test('should be noop when event target is text field', () => {
const wrapper = getWrapperWithSearchStrings();
wrapper.setState({ focusedIndex: 3 });
const instance = wrapper.instance();
instance.handleKeyboardSearch({
target: { hasAttribute: () => false, nodeName },
key: 'a',
});
expect(wrapper.state('focusedIndex')).toEqual(3); // should not change
});
});
});
describe('render()', () => {
test('should add "is-selectable" class and pass props to table', () => {
const wrapper = getWrapper({});
const instance = wrapper.instance();
wrapper.setState({ focusedIndex: 1 });
const table = wrapper.find('Table');
expect(table.hasClass('is-selectable')).toBe(true);
expect(table.prop('onCheckboxClick')).toEqual(instance.handleCheckboxClick);
expect(table.prop('onRowClick')).toEqual(wrapper.instance().handleRowClick);
expect(table.prop('onRowFocus')).toEqual(wrapper.instance().handleRowFocus);
expect(table.prop('focusedItem')).toEqual('b');
expect(table.prop('focusedIndex')).toEqual(1);
expect(table.prop('onTableBlur')).toBe(instance.handleTableBlur);
expect(table.prop('onTableFocus')).toBe(instance.handleTableFocus);
});
});
});