mirror of
https://github.com/Choices-js/Choices.git
synced 2024-04-25 10:50:34 +02:00
* Update value with character value
* Remove .only
* Lowercase character before updating value
* Add cypress tests covering change
* Update logic to affect both select inputs
* Update cypress
* Emphasise remove button focus
* Text change
* Revert "Update cypress"
This reverts commit 81e406de85
.
* Remove false positive tests
This commit is contained in:
parent
85f0b5f9be
commit
67266a3aae
|
@ -5,13 +5,6 @@ describe('Choices - select one', () => {
|
|||
|
||||
describe('scenarios', () => {
|
||||
describe('basic', () => {
|
||||
beforeEach(() => {
|
||||
// open dropdown
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices')
|
||||
.click();
|
||||
});
|
||||
|
||||
describe('focusing on container', () => {
|
||||
describe('pressing enter key', () => {
|
||||
it('toggles the dropdown', () => {
|
||||
|
@ -22,7 +15,7 @@ describe('Choices - select one', () => {
|
|||
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__list--dropdown')
|
||||
.should('not.be.visible');
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices')
|
||||
|
@ -31,42 +24,38 @@ describe('Choices - select one', () => {
|
|||
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__list--dropdown')
|
||||
.should('be.visible');
|
||||
.should('not.be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('focusing on text input', () => {
|
||||
it('displays a dropdown of choices', () => {
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__list--dropdown')
|
||||
.should('be.visible');
|
||||
describe('pressing an alpha-numeric key', () => {
|
||||
it('opens the dropdown and the input value', () => {
|
||||
const inputValue = 'test';
|
||||
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__list--dropdown .choices__list')
|
||||
.children()
|
||||
.should('have.length', 4)
|
||||
.each(($choice, index) => {
|
||||
expect($choice.text().trim()).to.equal(`Choice ${index + 1}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pressing escape', () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__input--cloned')
|
||||
.type('{esc}');
|
||||
});
|
||||
.find('.choices')
|
||||
.focus()
|
||||
.type(inputValue);
|
||||
|
||||
it('closes the dropdown', () => {
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__list--dropdown')
|
||||
.should('not.be.visible');
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__input--cloned')
|
||||
.should('have.value', inputValue);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('selecting choices', () => {
|
||||
beforeEach(() => {
|
||||
// open dropdown
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices')
|
||||
.click();
|
||||
});
|
||||
|
||||
const selectedChoiceText = 'Choice 1';
|
||||
|
||||
it('allows selecting choices from dropdown', () => {
|
||||
|
@ -102,6 +91,13 @@ describe('Choices - select one', () => {
|
|||
});
|
||||
|
||||
describe('searching choices', () => {
|
||||
beforeEach(() => {
|
||||
// open dropdown
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices')
|
||||
.click();
|
||||
});
|
||||
|
||||
describe('on input', () => {
|
||||
describe('searching by label', () => {
|
||||
it('displays choices filtered by inputted value', () => {
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 10em;
|
||||
opacity: 0.5;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.choices[data-type*='select-one'] .choices__button:hover, .choices[data-type*='select-one'] .choices__button:focus {
|
||||
|
|
2
public/assets/styles/choices.min.css
vendored
2
public/assets/styles/choices.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -1327,6 +1327,7 @@ class Choices {
|
|||
const hasActiveDropdown = this.dropdown.isActive;
|
||||
const hasItems = this.itemList.hasChildren();
|
||||
const keyString = String.fromCharCode(keyCode);
|
||||
const wasAlphaNumericChar = /[a-zA-Z0-9-_ ]/.test(keyString);
|
||||
|
||||
const {
|
||||
BACK_KEY,
|
||||
|
@ -1340,9 +1341,17 @@ class Choices {
|
|||
PAGE_DOWN_KEY,
|
||||
} = KEY_CODES;
|
||||
|
||||
// If a user is typing and the dropdown is not active
|
||||
if (!this._isTextElement && /[a-zA-Z0-9-_ ]/.test(keyString)) {
|
||||
if (!this._isTextElement && !hasActiveDropdown && wasAlphaNumericChar) {
|
||||
this.showDropdown();
|
||||
|
||||
if (!this.input.isFocussed) {
|
||||
/*
|
||||
We update the input value with the pressed key as
|
||||
the input was not focussed at the time of key press
|
||||
therefore does not have the value of the key.
|
||||
*/
|
||||
this.input.value += keyString.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
// Map keys to key actions
|
||||
|
@ -1358,7 +1367,6 @@ class Choices {
|
|||
[BACK_KEY]: this._onDeleteKey,
|
||||
};
|
||||
|
||||
// If keycode has a function, run it
|
||||
if (keyDownActions[keyCode]) {
|
||||
keyDownActions[keyCode]({
|
||||
event,
|
||||
|
@ -1380,6 +1388,7 @@ class Choices {
|
|||
// notice. Otherwise hide the dropdown
|
||||
if (this._isTextElement) {
|
||||
const canShowDropdownNotice = canAddItem.notice && value;
|
||||
|
||||
if (canShowDropdownNotice) {
|
||||
const dropdownItem = this._getTemplate('notice', canAddItem.notice);
|
||||
this.dropdown.element.innerHTML = dropdownItem.outerHTML;
|
||||
|
@ -1388,8 +1397,8 @@ class Choices {
|
|||
this.hideDropdown(true);
|
||||
}
|
||||
} else {
|
||||
const userHasRemovedValue =
|
||||
(keyCode === backKey || keyCode === deleteKey) && !target.value;
|
||||
const wasRemovalKeyCode = keyCode === backKey || keyCode === deleteKey;
|
||||
const userHasRemovedValue = wasRemovalKeyCode && !target.value;
|
||||
const canReactivateChoices = !this._isTextElement && this._isSearching;
|
||||
const canSearch = this._canSearch && canAddItem.response;
|
||||
|
||||
|
@ -1407,6 +1416,7 @@ class Choices {
|
|||
_onAKey({ event, hasItems }) {
|
||||
const { ctrlKey, metaKey } = event;
|
||||
const hasCtrlDownKeyPressed = ctrlKey || metaKey;
|
||||
|
||||
// If CTRL + A or CMD + A have been pressed and there are items to select
|
||||
if (hasCtrlDownKeyPressed && hasItems) {
|
||||
this._canSearch = false;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { spy, stub } from 'sinon';
|
|||
import sinonChai from 'sinon-chai';
|
||||
|
||||
import Choices from './choices';
|
||||
import { EVENTS, ACTION_TYPES, DEFAULT_CONFIG } from './constants';
|
||||
import { EVENTS, ACTION_TYPES, DEFAULT_CONFIG, KEY_CODES } from './constants';
|
||||
import { WrappedSelect, WrappedInput } from './components/index';
|
||||
|
||||
chai.use(sinonChai);
|
||||
|
@ -2025,5 +2025,135 @@ describe('choices', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_onKeyDown', () => {
|
||||
beforeEach(() => {
|
||||
instance.showDropdown = stub();
|
||||
instance._onAKey = stub();
|
||||
instance._onEnterKey = stub();
|
||||
instance._onEscapeKey = stub();
|
||||
instance._onDirectionKey = stub();
|
||||
instance._onDeleteKey = stub();
|
||||
});
|
||||
|
||||
const scenarios = [
|
||||
{
|
||||
keyCode: KEY_CODES.BACK_KEY,
|
||||
expectedFunctionCall: '_onDeleteKey',
|
||||
},
|
||||
{
|
||||
keyCode: KEY_CODES.DELETE_KEY,
|
||||
expectedFunctionCall: '_onDeleteKey',
|
||||
},
|
||||
{
|
||||
keyCode: KEY_CODES.A_KEY,
|
||||
expectedFunctionCall: '_onAKey',
|
||||
},
|
||||
{
|
||||
keyCode: KEY_CODES.ENTER_KEY,
|
||||
expectedFunctionCall: '_onEnterKey',
|
||||
},
|
||||
{
|
||||
keyCode: KEY_CODES.UP_KEY,
|
||||
expectedFunctionCall: '_onDirectionKey',
|
||||
},
|
||||
{
|
||||
keyCode: KEY_CODES.DOWN_KEY,
|
||||
expectedFunctionCall: '_onDirectionKey',
|
||||
},
|
||||
{
|
||||
keyCode: KEY_CODES.DOWN_KEY,
|
||||
expectedFunctionCall: '_onDirectionKey',
|
||||
},
|
||||
{
|
||||
keyCode: KEY_CODES.ESC_KEY,
|
||||
expectedFunctionCall: '_onEscapeKey',
|
||||
},
|
||||
];
|
||||
|
||||
describe('when called with a keydown event', () => {
|
||||
scenarios.forEach(({ keyCode, expectedFunctionCall }) => {
|
||||
describe(`when the keyCode is ${keyCode}`, () => {
|
||||
it(`calls ${expectedFunctionCall} with the expected arguments`, () => {
|
||||
const mockEvent = {
|
||||
keyCode,
|
||||
};
|
||||
|
||||
instance._onKeyDown(mockEvent);
|
||||
|
||||
expect(instance[expectedFunctionCall]).to.have.been.calledWith({
|
||||
event: mockEvent,
|
||||
activeItems: instance._store.activeItems,
|
||||
hasActiveDropdown: instance.dropdown.isActive,
|
||||
hasFocusedInput: instance.input.isFocussed,
|
||||
hasItems: instance.itemList.hasChildren(),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('select input', () => {
|
||||
describe('when the dropdown is not active', () => {
|
||||
describe('when the key was alpha-numeric', () => {
|
||||
beforeEach(() => {
|
||||
instance._isTextElement = false;
|
||||
instance.dropdown.isActive = false;
|
||||
});
|
||||
|
||||
it('shows the dropdown', () => {
|
||||
instance._onKeyDown({
|
||||
keyCode: KEY_CODES.A_KEY,
|
||||
});
|
||||
|
||||
expect(instance.showDropdown).to.have.been.calledWith();
|
||||
});
|
||||
|
||||
describe('when the input is not focussed', () => {
|
||||
beforeEach(() => {
|
||||
instance.input.isFocussed = false;
|
||||
});
|
||||
|
||||
it('updates the input value with the character corresponding to the key code', () => {
|
||||
instance._onKeyDown({
|
||||
keyCode: KEY_CODES.A_KEY,
|
||||
});
|
||||
|
||||
expect(instance.input.value).to.contain('a');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the input is focussed', () => {
|
||||
beforeEach(() => {
|
||||
instance.input.isFocussed = true;
|
||||
});
|
||||
|
||||
it('does not update the input value', () => {
|
||||
instance._onKeyDown({
|
||||
keyCode: KEY_CODES.A_KEY,
|
||||
});
|
||||
|
||||
expect(instance.input.value).to.not.contain('a');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the input was not alpha-numeric', () => {
|
||||
beforeEach(() => {
|
||||
instance._isTextElement = false;
|
||||
instance.dropdown.isActive = false;
|
||||
});
|
||||
|
||||
it('does not show the dropdown', () => {
|
||||
instance._onKeyDown({
|
||||
keyCode: KEY_CODES.DELETE_KEY,
|
||||
});
|
||||
|
||||
expect(instance.showDropdown).to.not.have.been.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -74,7 +74,7 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 10em;
|
||||
opacity: 0.5;
|
||||
opacity: 0.25;
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
|
|
Loading…
Reference in a new issue