@guardian/threads
Version:
283 lines • 17 kB
JavaScript
import { __awaiter, __generator } from "tslib";
import { fireEvent, waitFor, queryHelpers, act } from '@testing-library/react';
import { renderFocusedAsyncChip, renderFocusedAsyncChipWithMenu, keyCodes, } from './selectAsyncChipHelpers';
import { getEditButtonTestId } from '../SelectAsyncChip';
import { stagedForDeletionKey } from '../ChipWrapper';
import { menuItemTestId, menuItemEmulateHoverKey, } from '../../../../Menu/MenuItem';
var onInputChange = jest.fn(function (_) {
return Promise.resolve([
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
]);
});
describe('SelectAsyncChip', function () {
describe('navigation between chips', function () {
it('should focus the input and select its contents when entered from the left hand side', function () { return __awaiter(void 0, void 0, void 0, function () {
var _a, selectAsyncChipInput, lhsInlineInput;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, renderFocusedAsyncChipWithMenu({
onInputChange: onInputChange,
})];
case 1:
_a = _b.sent(), selectAsyncChipInput = _a.selectAsyncChipInput, lhsInlineInput = _a.lhsInlineInput;
fireEvent.keyDown(selectAsyncChipInput, keyCodes.Enter); // Select item '1'
lhsInlineInput.selectionStart = lhsInlineInput.selectionEnd = 3;
lhsInlineInput.focus();
fireEvent.keyDown(lhsInlineInput, keyCodes.ArrowRight);
// Item '1' is now present and selected
return [4 /*yield*/, waitFor(function () {
expect(selectAsyncChipInput.selectionStart).toEqual(0);
expect(selectAsyncChipInput.selectionEnd).toEqual(1);
expect(document.activeElement).toBe(selectAsyncChipInput);
})];
case 2:
// Item '1' is now present and selected
_b.sent();
return [2 /*return*/];
}
});
}); });
it('should focus the input and select its contents when entered from the right hand side', function () { return __awaiter(void 0, void 0, void 0, function () {
var _a, selectAsyncChipInput, rhsInlineInput;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, renderFocusedAsyncChipWithMenu({
onInputChange: onInputChange,
})];
case 1:
_a = _b.sent(), selectAsyncChipInput = _a.selectAsyncChipInput, rhsInlineInput = _a.rhsInlineInput;
fireEvent.keyDown(selectAsyncChipInput, keyCodes.Enter); // Select item '1'
rhsInlineInput.focus();
fireEvent.keyDown(rhsInlineInput, keyCodes.ArrowLeft);
// Item '1' is now present and selected
expect(selectAsyncChipInput.selectionStart).toEqual(0);
expect(selectAsyncChipInput.selectionEnd).toEqual(1);
expect(document.activeElement).toBe(selectAsyncChipInput);
return [2 /*return*/];
}
});
}); });
it('should focus the next chip at the start of the input when a right arrow key is pressed and the cursor is at the end of the input value', function () {
var _a = renderFocusedAsyncChip(), selectAsyncChipInput = _a.selectAsyncChipInput, rhsInlineInput = _a.rhsInlineInput;
selectAsyncChipInput.selectionStart = selectAsyncChipInput.selectionEnd = 7;
fireEvent.keyDown(selectAsyncChipInput, keyCodes.ArrowRight);
expect(document.activeElement).toEqual(rhsInlineInput);
expect(rhsInlineInput.selectionStart).toBe(0);
});
it('should focus the previous chip when a left arrow key is pressed and the cursor is at the beginning of the input value', function () {
var _a = renderFocusedAsyncChip(), selectAsyncChipInput = _a.selectAsyncChipInput, lhsInlineInput = _a.lhsInlineInput;
fireEvent.keyDown(selectAsyncChipInput, keyCodes.ArrowLeft);
expect(document.activeElement).toEqual(lhsInlineInput);
expect(lhsInlineInput.selectionStart).toBe(3);
});
});
describe('input', function () {
it('should render an input and present its value', function () {
var selectAsyncChipInput = renderFocusedAsyncChip().selectAsyncChipInput;
expect(selectAsyncChipInput.value).toBe('example');
});
it('should call onInputChanged when the input changes', function () {
var _onInputChange = jest.fn(function (_) { return Promise.resolve([]); });
renderFocusedAsyncChip({
onInputChange: _onInputChange,
});
// A change event is fired when renderFocusedAsyncChip is called,
// to set the initial value of the input.
expect(_onInputChange.mock.calls[0][0]).toBe('example');
});
it('should enter edit mode if a selection has been made and the element is focused', function () {
var _a = renderFocusedAsyncChip(), selectAsyncChipInput = _a.selectAsyncChipInput, queryByTestId = _a.queryByTestId;
expect(document.activeElement).toEqual(selectAsyncChipInput);
expect(selectAsyncChipInput.selectionStart).toEqual(0);
expect(selectAsyncChipInput.selectionEnd).toEqual(0);
expect(queryByTestId(getEditButtonTestId(1))).toBe(null);
});
it("should keep its input value, and display an 'invalid' state, when it loses focus and no option is selected", function () { return __awaiter(void 0, void 0, void 0, function () {
var _a, selectAsyncChipInput, selectAsyncChipWrapper, rhsInlineInput;
return __generator(this, function (_b) {
_a = renderFocusedAsyncChip({ value: '' }), selectAsyncChipInput = _a.selectAsyncChipInput, selectAsyncChipWrapper = _a.selectAsyncChipWrapper, rhsInlineInput = _a.rhsInlineInput;
selectAsyncChipInput.selectionStart = selectAsyncChipInput.selectionEnd = 7;
act(function () {
fireEvent.change(selectAsyncChipInput, {
target: { value: 'omg' },
});
fireEvent.keyDown(selectAsyncChipInput, keyCodes.ArrowRight);
});
expect(document.activeElement).toEqual(rhsInlineInput);
expect(selectAsyncChipInput.value).toEqual('omg');
expect(selectAsyncChipWrapper.getAttribute('data-invalid')).toBe('true');
return [2 /*return*/];
});
}); });
it('should revert to the previously selected value when it loses focus and no option is selected', function () { return __awaiter(void 0, void 0, void 0, function () {
var _a, menuItems, selectAsyncChipInput, lhsInlineInput;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, renderFocusedAsyncChipWithMenu({
onInputChange: onInputChange,
})];
case 1:
_a = _b.sent(), menuItems = _a.menuItems, selectAsyncChipInput = _a.selectAsyncChipInput, lhsInlineInput = _a.lhsInlineInput;
fireEvent.click(menuItems[0]); // Select item '1'
selectAsyncChipInput.focus(); // Enter edit mode
fireEvent.change(selectAsyncChipInput, {
target: { value: 'something else' },
}); // Change some text
lhsInlineInput.focus(); // Focus elsewhere without selecting an option
return [4 /*yield*/, waitFor(function () { return expect(selectAsyncChipInput.value).toEqual('1'); })];
case 2:
_b.sent();
return [2 /*return*/];
}
});
}); });
describe('character deletion', function () {
/**
* Test whether the async chip is staged for deletion given a combination of chip
* input value and keyDown event.
*/
var testIfKeydownStagesDeletion = function (value, keyCode) {
var opts = renderFocusedAsyncChip({
value: value,
});
fireEvent.keyDown(opts.selectAsyncChipInput, keyCode);
return queryHelpers.queryByAttribute(stagedForDeletionKey, opts.container, 'true');
};
[keyCodes.Delete, keyCodes.Backspace].forEach(function (keyCode) {
it("should not stage deletion if the input is not empty and a " + keyCode.key + " keydown event is detected", function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
expect(testIfKeydownStagesDeletion('input has content', keyCode)).toBe(null);
return [2 /*return*/];
});
}); });
it("should stage deletion if the input is empty and a " + keyCode.key + " keydown event is detected", function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
expect(testIfKeydownStagesDeletion('', keyCode)).not.toBe(null);
return [2 /*return*/];
});
}); });
});
});
});
describe('option display and selection', function () {
it('should display a menu when options are received', function () { return __awaiter(void 0, void 0, void 0, function () {
var queryAllByTestId;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, renderFocusedAsyncChipWithMenu({
onInputChange: onInputChange,
})];
case 1:
queryAllByTestId = (_a.sent()).queryAllByTestId;
expect(queryAllByTestId(menuItemTestId).length).toBe(3);
return [2 /*return*/];
}
});
}); });
it('should highlight the first option in the menu by default', function () { return __awaiter(void 0, void 0, void 0, function () {
var queryAllByTestId, menuItems;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, renderFocusedAsyncChipWithMenu({
onInputChange: onInputChange,
})];
case 1:
queryAllByTestId = (_a.sent()).queryAllByTestId;
menuItems = queryAllByTestId(menuItemTestId);
expect(menuItems[0].getAttribute(menuItemEmulateHoverKey)).toBe('true');
expect(menuItems[1].getAttribute(menuItemEmulateHoverKey)).toBe(null);
expect(menuItems[2].getAttribute(menuItemEmulateHoverKey)).toBe(null);
return [2 /*return*/];
}
});
}); });
it('should allow navigation of options with the arrow keys', function () { return __awaiter(void 0, void 0, void 0, function () {
var _a, queryAllByTestId, selectAsyncChipInput, menuItems;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, renderFocusedAsyncChipWithMenu({ onInputChange: onInputChange })];
case 1:
_a = _b.sent(), queryAllByTestId = _a.queryAllByTestId, selectAsyncChipInput = _a.selectAsyncChipInput;
fireEvent.keyDown(selectAsyncChipInput, keyCodes.ArrowDown);
fireEvent.keyDown(selectAsyncChipInput, keyCodes.ArrowDown);
menuItems = queryAllByTestId(menuItemTestId);
expect(menuItems[0].getAttribute(menuItemEmulateHoverKey)).toBe(null);
expect(menuItems[1].getAttribute(menuItemEmulateHoverKey)).toBe(null);
expect(menuItems[2].getAttribute(menuItemEmulateHoverKey)).toBe('true');
fireEvent.keyDown(selectAsyncChipInput, keyCodes.ArrowUp);
expect(menuItems[0].getAttribute(menuItemEmulateHoverKey)).toBe(null);
expect(menuItems[1].getAttribute(menuItemEmulateHoverKey)).toBe('true');
expect(menuItems[2].getAttribute(menuItemEmulateHoverKey)).toBe(null);
return [2 /*return*/];
}
});
}); });
it('should call onUpdate() when the enter key is pressed and an option is highlighted', function () { return __awaiter(void 0, void 0, void 0, function () {
var onUpdate, selectAsyncChipInput, queryElements;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
onUpdate = jest.fn();
return [4 /*yield*/, renderFocusedAsyncChipWithMenu({
onInputChange: onInputChange,
onUpdate: onUpdate,
})];
case 1:
selectAsyncChipInput = (_a.sent()).selectAsyncChipInput;
fireEvent.keyDown(selectAsyncChipInput, keyCodes.ArrowDown);
fireEvent.keyDown(selectAsyncChipInput, keyCodes.Enter);
queryElements = onUpdate.mock.calls[0][0];
return [4 /*yield*/, waitFor(function () { return expect(queryElements[1].value).toEqual('2'); })];
case 2:
_a.sent();
return [2 /*return*/];
}
});
}); });
it('should call onUpdate() when an option is clicked', function () { return __awaiter(void 0, void 0, void 0, function () {
var onUpdate, menuItems, queryElements;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
onUpdate = jest.fn();
return [4 /*yield*/, renderFocusedAsyncChipWithMenu({
onInputChange: onInputChange,
onUpdate: onUpdate,
})];
case 1:
menuItems = (_a.sent()).menuItems;
fireEvent.click(menuItems[0]);
queryElements = onUpdate.mock.calls[0][0];
return [4 /*yield*/, waitFor(function () { return expect(queryElements[1].value).toEqual('1'); })];
case 2:
_a.sent();
return [2 /*return*/];
}
});
}); });
it('should select the highlighted option if the chip loses focus', function () { return __awaiter(void 0, void 0, void 0, function () {
var onUpdate, selectAsyncChipInput, queryElements;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
onUpdate = jest.fn();
return [4 /*yield*/, renderFocusedAsyncChipWithMenu({
onUpdate: onUpdate,
onInputChange: onInputChange
})];
case 1:
selectAsyncChipInput = (_a.sent()).selectAsyncChipInput;
fireEvent.keyDown(selectAsyncChipInput, keyCodes.ArrowLeft);
queryElements = onUpdate.mock.calls[0][0];
return [4 /*yield*/, waitFor(function () { return expect(queryElements[1].value).toEqual('1'); })];
case 2:
_a.sent();
return [2 /*return*/];
}
});
}); });
});
});
//# sourceMappingURL=SelectAsyncChip.spec.js.map