diff --git a/lib/MultiSelection/MultiSelection.js b/lib/MultiSelection/MultiSelection.js
index 3bc8e673a..feec32890 100644
--- a/lib/MultiSelection/MultiSelection.js
+++ b/lib/MultiSelection/MultiSelection.js
@@ -166,7 +166,12 @@ const MultiSelection = ({
boil each object down to a string, so that the strict-equals will
have a better time succeeding. */
onSelectedItemsChange(changes) {
- onChange(changes.selectedItems);
+ // only trigger onChange if selectedItems is different from from the incoming value prop.
+ // this avoids instances when the value can appear *not to change for the user but it's really the cause of
+ // values just changing between re-renders and react just catching whichever onChange was triggered last.
+ if (!isEqual(value, changes.selectedItems)) {
+ onChange(changes.selectedItems);
+ }
awaitingChange.current = false;
},
onStateChange({ selectedItems: newSelectedItems, type }) {
@@ -422,7 +427,7 @@ const MultiSelection = ({
getInputProps={getInputProps}
getDropdownProps={getDropdownProps}
isOpen={isOpen}
- placeholder={selectedItems.length === 0 && placeholder}
+ placeholder={selectedItems.length === 0 ? placeholder : ''}
menuId={menuId}
setFilterValue={setFilterValue}
setFilterFocus={setFilterFocused}
diff --git a/lib/MultiSelection/tests/MultiSelection-test.js b/lib/MultiSelection/tests/MultiSelection-test.js
index 85b893290..e60191147 100644
--- a/lib/MultiSelection/tests/MultiSelection-test.js
+++ b/lib/MultiSelection/tests/MultiSelection-test.js
@@ -73,655 +73,657 @@ const listOptions = [
const testId = 'testingId';
describe('MultiSelect', () => {
- const onRemove = sinon.spy();
- const onChange = sinon.spy();
- beforeEach(async () => {
- await onRemove.resetHistory();
- await onChange.resetHistory();
- await mountWithContext(
-
- );
- });
+ describe('basic rendering', () => {
+ const onRemove = sinon.spy();
+ const onChange = sinon.spy();
+ beforeEach(async () => {
+ await onRemove.resetHistory();
+ await onChange.resetHistory();
+ await mountWithContext(
+
+ );
+ });
- it('contains no axe errors - Multiselect', runAxeTest);
+ it('contains no axe errors - Multiselect', runAxeTest);
- it('renders the control', () => multiselectionAriaLabelledby.exists());
+ it('renders the control', () => multiselectionAriaLabelledby.exists());
- it('does not have a value', () => multiselectionAriaLabelledby.has({ selectedCount: 0 }));
+ it('does not have a value', () => multiselectionAriaLabelledby.has({ selectedCount: 0 }));
- it('renders the supplied id prop', () => multiselectionAriaLabelledby.has({ id: testId }));
+ it('renders the supplied id prop', () => multiselectionAriaLabelledby.has({ id: testId }));
- it('renders placeholder', () => multiselectionAriaLabelledby.has({ placeholder: 'test multiselect' }));
+ it('renders placeholder', () => multiselectionAriaLabelledby.has({ placeholder: 'test multiselect' }));
- it('list is hidden by default', expectClosedMenu);
+ it('list is hidden by default', expectClosedMenu);
- it('control\'s aria-labelledBy attribute is set', () => multiselectionAriaLabelledby.has({ ariaLabelledby: including('test-label-id') }));
+ it('control\'s aria-labelledBy attribute is set', () => multiselectionAriaLabelledby.has({ ariaLabelledby: including('test-label-id') }));
- it('should have empty hidden value', () => hiddenInput.has({ value: '' }));
+ it('should have empty hidden value', () => hiddenInput.has({ value: '' }));
- describe('clicking the control', () => {
- beforeEach(async () => {
- await multiselectionAriaLabelledby.toggle();
- });
+ describe('clicking the control', () => {
+ beforeEach(async () => {
+ await multiselectionAriaLabelledby.toggle();
+ });
- it('opens the list', expectOpenMenu);
+ it('opens the list', expectOpenMenu);
- it('contains no axe errors - Multiselect: open menu', runAxeTest);
+ it('contains no axe errors - Multiselect: open menu', runAxeTest);
- it('focuses the filter input', () => multiselection.has({ focused: true }));
+ it('focuses the filter input', () => multiselection.has({ focused: true }));
- it(`list is rendered with ${listOptions.length} options`, () => menu.has({ optionCount: listOptions.length }));
+ it(`list is rendered with ${listOptions.length} options`, () => menu.has({ optionCount: listOptions.length }));
- describe('clicking an option', () => {
- beforeEach(async () => {
- await multiselection.select('Option 2');
- });
+ describe('clicking an option', () => {
+ beforeEach(async () => {
+ await multiselection.select('Option 2');
+ });
- it(`sets control value to ${listOptions[2].label}`, () => ValueChipInteractor(`${listOptions[2].label}`).exists());
+ it(`sets control value to ${listOptions[2].label}`, () => ValueChipInteractor(`${listOptions[2].label}`).exists());
- it('the list stays open', expectOpenMenu);
+ it('the list stays open', expectOpenMenu);
- it('does not render placeholder', () => multiselection.has({ placeholder: '' }));
+ it('does not render placeholder', () => multiselection.has({ placeholder: '' }));
- it('sets correct value of hidden input value', () => hiddenInput.has({ value: listOptions[2].label }));
+ it('sets correct value of hidden input value', () => hiddenInput.has({ value: listOptions[2].label }));
- it('calls the onChange handler supplying the selected object', async () => {
- await converge(() => {
- if (!onChange.calledOnceWith([listOptions[2]])) throw new Error('expected onChange handler to be called with array of selected values');
+ it('calls the onChange handler supplying the selected object', async () => {
+ await converge(() => {
+ if (!onChange.calledOnceWith([listOptions[2]])) throw new Error('expected onChange handler to be called with array of selected values');
+ });
});
});
- });
- describe('clicking multiple options', () => {
- beforeEach(async () => {
- await multiselection.select([
- `${listOptions[2].label}`,
- `${listOptions[3].label}`,
- `${listOptions[4].label}`
- ])
- });
-
- it(`sets control value to ${listOptions[2].label}, ${listOptions[3].label}, ${listOptions[4].label}`, async () => {
- return Promise.all(
- [
- multiselection.has({ selectedCount: 3 }),
- multiselection.has({ selected: [`${listOptions[2].label}`, `${listOptions[3].label}`, `${listOptions[4].label}`] })
- ]
- );
- });
-
- it('the list stays open', expectOpenMenu);
-
- describe('Keyboard: Backspace to remove values', () => {
+ describe('clicking multiple options', () => {
beforeEach(async () => {
- await multiselection.focus();
- await Keyboard.backspace();
+ await multiselection.select([
+ `${listOptions[2].label}`,
+ `${listOptions[3].label}`,
+ `${listOptions[4].label}`
+ ])
});
- it('removes the last selected item', () => {
+ it(`sets control value to ${listOptions[2].label}, ${listOptions[3].label}, ${listOptions[4].label}`, async () => {
return Promise.all(
[
- multiselection.has({ selectedCount: 2 }),
- multiselection.has({ selected: [`${listOptions[2].label}`, `${listOptions[3].label}`] })
+ multiselection.has({ selectedCount: 3 }),
+ multiselection.has({ selected: [`${listOptions[2].label}`, `${listOptions[3].label}`, `${listOptions[4].label}`] })
]
);
});
- it('calls the supplied onRemove handler, supplying the removed item.', () => {
- expect(onRemove.calledOnceWith(listOptions[4])).to.equal(true);
+ it('the list stays open', expectOpenMenu);
+
+ describe('Keyboard: Backspace to remove values', () => {
+ beforeEach(async () => {
+ await multiselection.focus();
+ await Keyboard.backspace();
+ });
+
+ it('removes the last selected item', () => {
+ return Promise.all(
+ [
+ multiselection.has({ selectedCount: 2 }),
+ multiselection.has({ selected: [`${listOptions[2].label}`, `${listOptions[3].label}`] })
+ ]
+ );
+ });
+
+ it('calls the supplied onRemove handler, supplying the removed item.', () => {
+ expect(onRemove.calledOnceWith(listOptions[4])).to.equal(true);
+ });
});
- });
- describe('Clicking the remove button on the first value chip', () => {
- beforeEach(async () => {
- await ValueChipInteractor({ index: 0 }).remove();
+ describe('Clicking the remove button on the first value chip', () => {
+ beforeEach(async () => {
+ await ValueChipInteractor({ index: 0 }).remove();
+ });
+
+ it('calls the supplied onRemove handler, supplying the removed item.', () => {
+ expect(onRemove.calledOnceWith(listOptions[2])).to.equal(true);
+ });
});
- it('calls the supplied onRemove handler, supplying the removed item.', () => {
- expect(onRemove.calledOnceWith(listOptions[2])).to.equal(true);
+ describe('Clicking the selected item in the options list', () => {
+ beforeEach(async () => {
+ await multiselection.toggle();
+ await OptionInteractor(listOptions[3].label).click();
+ await multiselection.has({ selectedCount: 2 });
+ });
+ it('calls the supplied onRemove handler, supplying the removed item.', () => {
+ expect(onRemove.calledOnceWith(listOptions[3])).to.equal(true);
+ });
+ it('has option 3 selected', () => OptionInteractor(listOptions[3].label).has({ selected: false }));
});
});
- describe('Clicking the selected item in the options list', () => {
+ describe('clicking the toggleButton with the open menu', () => {
beforeEach(async () => {
await multiselection.toggle();
- await OptionInteractor(listOptions[3].label).click();
- await multiselection.has({ selectedCount: 2 });
- });
- it('calls the supplied onRemove handler, supplying the removed item.', () => {
- expect(onRemove.calledOnceWith(listOptions[3])).to.equal(true);
});
- it('has option 3 selected', () => OptionInteractor(listOptions[3].label).has({ selected: false }));
- });
- });
- describe('clicking the toggleButton with the open menu', () => {
- beforeEach(async () => {
- await multiselection.toggle();
+ it('closes the list', expectClosedMenu);
});
- it('closes the list', expectClosedMenu);
- });
-
- describe('filtering options', () => {
- beforeEach(async () => {
- await multiselection.fillIn('sample');
- });
+ describe('filtering options', () => {
+ beforeEach(async () => {
+ await multiselection.fillIn('sample');
+ });
- it('first option is cursored', () => OptionInteractor({ index: 0, cursored: true }).exists());
+ it('first option is cursored', () => OptionInteractor({ index: 0, cursored: true }).exists());
- it('decreases list to 3 options', () => menu.has({ optionCount: 3 }));
+ it('decreases list to 3 options', () => menu.has({ optionCount: 3 }));
- it('does not display the empty message', () => emptyMessage().absent());
+ it('does not display the empty message', () => emptyMessage().absent());
- describe('clicking a filtered option', () => {
- beforeEach(async () => {
- await OptionInteractor({ index: 2 }).click();
- });
+ describe('clicking a filtered option', () => {
+ beforeEach(async () => {
+ await OptionInteractor({ index: 2 }).click();
+ });
- it('sets the value appropriately', () => {
- multiselection.has({ selectedCount: 1 });
- OptionInteractor({ selected: [`${listOptions[5].label}`] });
+ it('sets the value appropriately', () => {
+ multiselection.has({ selectedCount: 1 });
+ OptionInteractor({ selected: [`${listOptions[5].label}`] });
+ });
});
- });
- describe('No options available after filtering', () => {
- beforeEach(async () => {
- await multiselection.filter('none');
- });
+ describe('No options available after filtering', () => {
+ beforeEach(async () => {
+ await multiselection.filter('none');
+ });
- it('displays the empty message', () => {
- emptyMessage().exists();
+ it('displays the empty message', () => {
+ emptyMessage().exists();
+ });
});
});
});
});
-});
-
-describe('supplying a label prop', () => {
- beforeEach(async () => {
- await mountWithContext(
-
- );
- });
- it('renders the label', () => {
- MultiSelectInteractor('test selection').exists();
- });
+ describe('supplying a label prop', () => {
+ beforeEach(async () => {
+ await mountWithContext(
+
+ );
+ });
- it('control\'s aria-labelledBy attribute is set', () => {
- multiselection.has({ arialabelledBy: `${testId}-label multi-value-status-${testId}` });
- });
-});
+ it('renders the label', () => {
+ MultiSelectInteractor('test selection').exists();
+ });
-describe('supplying an aria-label prop', () => {
- beforeEach(async () => {
- await mountWithContext(
-
- );
+ it('control\'s aria-labelledBy attribute is set', () => {
+ multiselection.has({ arialabelledBy: `${testId}-label multi-value-status-${testId}` });
+ });
});
- it('renders the label', () => {
- MultiSelectInteractor('test aria selection').has({ visible: false });
- });
+ describe('supplying an aria-label prop', () => {
+ beforeEach(async () => {
+ await mountWithContext(
+
+ );
+ });
- it('control\'s aria-labelledBy attribute is set', () => {
- multiselection.has({ arialabelledBy: `${testId}-label multi-value-status-${testId}` });
- });
+ it('renders the label', () => {
+ MultiSelectInteractor('test aria selection').has({ visible: false });
+ });
- it('renders the label with an sr-only classname', () => Label('test aria selection').has({ className: including('sr-only'), visible: false }));
+ it('control\'s aria-labelledBy attribute is set', () => {
+ multiselection.has({ arialabelledBy: `${testId}-label multi-value-status-${testId}` });
+ });
- it('contains no axe errors - Multiselect: aria-label prop', runAxeTest);
-});
+ it('renders the label with an sr-only classname', () => Label('test aria selection').has({ className: including('sr-only'), visible: false }));
-describe('supplying an aria-labelledby prop', () => {
- const customLabelledBy = 'custom-aria-labelledby';
-
- beforeEach(async () => {
- await mountWithContext(
-
- );
+ it('contains no axe errors - Multiselect: aria-label prop', runAxeTest);
});
- it('applies the aria-labelledby to the control element', () => {
- multiselection.has({ ariaLabelledBy: including(customLabelledBy) });
- });
+ describe('supplying an aria-labelledby prop', () => {
+ const customLabelledBy = 'custom-aria-labelledby';
- it('applies the aria-labelledby to the filter input', () => {
- TextInput({ ariaLabelledBy: customLabelledBy }).exists();
- });
-});
+ beforeEach(async () => {
+ await mountWithContext(
+
+ );
+ });
-describe('MultiSelection, initial value', () => {
- beforeEach(async () => {
- await mountWithContext(
-
- );
- });
+ it('applies the aria-labelledby to the control element', () => {
+ multiselection.has({ ariaLabelledBy: including(customLabelledBy) });
+ });
- it('renders the selected options\' values', () => {
- multiselection.has({ selected: [listOptions[1].label, listOptions[3].label, listOptions[5].label] });
+ it('applies the aria-labelledby to the filter input', () => {
+ TextInput({ ariaLabelledBy: customLabelledBy }).exists();
+ });
});
- it('sets correct value in hidden input', () => {
- hiddenInput.has({ value:`${listOptions[1].label},${listOptions[3].label},${listOptions[5].label}` });
- });
+ describe('MultiSelection, initial value', () => {
+ beforeEach(async () => {
+ await mountWithContext(
+
+ );
+ });
- describe('Keyboard : navigating selected values', () => {
- describe('Keyboard: pressing the Home key when middle selected value is focused', () => {
- beforeEach(async () => {
- await ValueChipInteractor({ index: 1 }).focus();
- await Keyboard.home();
- });
+ it('renders the selected options\' values', () => {
+ multiselection.has({ selected: [listOptions[1].label, listOptions[3].label, listOptions[5].label] });
+ });
- it('focuses the first selected item', () => {
- ValueChipInteractor({ index: 0, focused: true }).exists();
- });
+ it('sets correct value in hidden input', () => {
+ hiddenInput.has({ value:`${listOptions[1].label},${listOptions[3].label},${listOptions[5].label}` });
+ });
- describe('Keyboard: pressing the End key while a selected value is focused', () => {
+ describe('Keyboard : navigating selected values', () => {
+ describe('Keyboard: pressing the Home key when middle selected value is focused', () => {
beforeEach(async () => {
- await Keyboard.end();
+ await ValueChipInteractor({ index: 1 }).focus();
+ await Keyboard.home();
});
- it('focuses the last selected item', () => {
- ValueChipInteractor({ index: 2, focused: true });
+ it('focuses the first selected item', () => {
+ ValueChipInteractor({ index: 0, focused: true }).exists();
});
- });
- });
- });
- describe('Clicking the remove button on a value chip', () => {
- beforeEach(async () => {
- await ValueChipInteractor({ index: 0 }).remove();
- });
+ describe('Keyboard: pressing the End key while a selected value is focused', () => {
+ beforeEach(async () => {
+ await Keyboard.end();
+ });
- it('removes the value from selection', () => {
- multiselection.has({ selectedCount: 2 });
- });
-
- it('moves focus to remaining option', () => {
- ValueChipInteractor({ index: 0, focused: true }).exists();
+ it('focuses the last selected item', () => {
+ ValueChipInteractor({ index: 2, focused: true });
+ });
+ });
+ });
});
- describe('Clicking the remove button on the last remaining value chip', () => {
+ describe('Clicking the remove button on a value chip', () => {
beforeEach(async () => {
await ValueChipInteractor({ index: 0 }).remove();
- await ValueChipInteractor({ index: 0 }).remove();
});
it('removes the value from selection', () => {
- multiselection.has({ selectedCount: 0 });
+ multiselection.has({ selectedCount: 2 });
});
- it('moves focus to the filter', () => {
- multiselection.is({ focused: true });
+ it('moves focus to remaining option', () => {
+ ValueChipInteractor({ index: 0, focused: true }).exists();
});
- });
- });
- describe('Keyboard : down arrow on control with menu closed', () => {
- beforeEach(async () => {
- await multiselection.focus();
- await Keyboard.arrowDown();
- });
+ describe('Clicking the remove button on the last remaining value chip', () => {
+ beforeEach(async () => {
+ await ValueChipInteractor({ index: 0 }).remove();
+ await ValueChipInteractor({ index: 0 }).remove();
+ });
- it('opens the selection menu', expectOpenMenu);
+ it('removes the value from selection', () => {
+ multiselection.has({ selectedCount: 0 });
+ });
- it('the cursor is on the first option', () => {
- OptionInteractor({ index: 0, cursored: true }).exists();
+ it('moves focus to the filter', () => {
+ multiselection.is({ focused: true });
+ });
+ });
});
- });
- describe('Keyboard : down arrow with open menu navigates next options', () => {
- beforeEach(async () => {
- // back twice to keep this from passing if the down arrow test fails.
- await multiselection.toggle();
- await Keyboard.arrowDown();
- await Keyboard.arrowDown();
- await Keyboard.arrowDown();
- });
+ describe('Keyboard : down arrow on control with menu closed', () => {
+ beforeEach(async () => {
+ await multiselection.focus();
+ await Keyboard.arrowDown();
+ });
- it('moves cursor the next option', () => {
- OptionInteractor({ index: 2, cursored: true }).exists();
- });
+ it('opens the selection menu', expectOpenMenu);
- it('sets the appropriate aria-activedescendant on the filter', () => {
- TextInput({ ariaActiveDescendent: OptionInteractor({ index: 2 }).id }).exists();
+ it('the cursor is on the first option', () => {
+ OptionInteractor({ index: 0, cursored: true }).exists();
+ });
});
- describe('Keyboard : up arrow with open menu navigates to previous option', () => {
+ describe('Keyboard : down arrow with open menu navigates next options', () => {
beforeEach(async () => {
- await Keyboard.arrowUp();
- await Keyboard.arrowUp();
+ // back twice to keep this from passing if the down arrow test fails.
+ await multiselection.toggle();
+ await Keyboard.arrowDown();
+ await Keyboard.arrowDown();
+ await Keyboard.arrowDown();
});
- it('moves cursor the previous option', () => {
- OptionInteractor({ index: 0, cursored: true }).exists();
+ it('moves cursor the next option', () => {
+ OptionInteractor({ index: 2, cursored: true }).exists();
});
it('sets the appropriate aria-activedescendant on the filter', () => {
- TextInput({ ariaActiveDescendent: OptionInteractor({ index: 0 }).id }).exists();
+ TextInput({ ariaActiveDescendent: OptionInteractor({ index: 2 }).id }).exists();
});
- describe('Keyboard : pressing enter with an open menu', () => {
+ describe('Keyboard : up arrow with open menu navigates to previous option', () => {
beforeEach(async () => {
- await Keyboard.enter();
+ await Keyboard.arrowUp();
+ await Keyboard.arrowUp();
});
- it('selects the option at the cursor', () => {
+ it('moves cursor the previous option', () => {
OptionInteractor({ index: 0, cursored: true }).exists();
- OptionInteractor({ index: 0, selected: true }).exists();
});
- it('adds the selection to the selected value list', () => {
- ValueChipInteractor('Sample 2').exists();
+ it('sets the appropriate aria-activedescendant on the filter', () => {
+ TextInput({ ariaActiveDescendent: OptionInteractor({ index: 0 }).id }).exists();
});
- });
- });
- describe('Keyboard: pressing End key with open menu', () => {
- beforeEach(async () => {
- await multiselection.toggle();
- await Keyboard.end();
- });
+ describe('Keyboard : pressing enter with an open menu', () => {
+ beforeEach(async () => {
+ await Keyboard.enter();
+ });
+
+ it('selects the option at the cursor', () => {
+ OptionInteractor({ index: 0, cursored: true }).exists();
+ OptionInteractor({ index: 0, selected: true }).exists();
+ });
- it('moves cursor to the last option', () => {
- OptionInteractor({ index: 5, cursored: true }).exists();
+ it('adds the selection to the selected value list', () => {
+ ValueChipInteractor('Sample 2').exists();
+ });
+ });
});
- describe('Keyboard: pressing Home key with open menu', () => {
+ describe('Keyboard: pressing End key with open menu', () => {
beforeEach(async () => {
await multiselection.toggle();
- await Keyboard.home();
+ await Keyboard.end();
});
it('moves cursor to the last option', () => {
- OptionInteractor({ index: 0, cursored: true }).exists();
+ OptionInteractor({ index: 5, cursored: true }).exists();
});
- });
- });
- });
-});
-describe('Filtering option list: cursor on first', () => {
- beforeEach(async () => {
- await multiselection.filter('sam');
- });
-
- it('sets cursor to first result', () => {
- OptionInteractor({ index: 0, cursored: true }).exists();
- });
+ describe('Keyboard: pressing Home key with open menu', () => {
+ beforeEach(async () => {
+ await multiselection.toggle();
+ await Keyboard.home();
+ });
- it('sets the appropriate aria-activedescendant on the filter', () => {
- TextInput({ ariaActiveDescendent: OptionInteractor({ index: 0 }).id }).exists();
+ it('moves cursor to the last option', () => {
+ OptionInteractor({ index: 0, cursored: true }).exists();
+ });
+ });
+ });
+ });
});
- describe('Keyboard control on filtered list: move cursor down', () => {
+ describe('Filtering option list: cursor on first', () => {
beforeEach(async () => {
- await Keyboard.arrowDown();
- await Keyboard.arrowDown();
+ await multiselection.filter('sam');
});
- it('sets cursor to third result', () => {
- // expect(selection.options(0).isCursored).to.be.false;
- OptionInteractor({ index: 2, cursored: true }).exists();
+ it('sets cursor to first result', () => {
+ OptionInteractor({ index: 0, cursored: true }).exists();
});
it('sets the appropriate aria-activedescendant on the filter', () => {
- TextInput({ ariaActiveDescendent: OptionInteractor({ index: 2 }).id }).exists();
+ TextInput({ ariaActiveDescendent: OptionInteractor({ index: 0 }).id }).exists();
});
- describe('Keyboard control on filtered list: move cursor up', () => {
+ describe('Keyboard control on filtered list: move cursor down', () => {
beforeEach(async () => {
- await Keyboard.arrowUp();
+ await Keyboard.arrowDown();
+ await Keyboard.arrowDown();
});
- it('sets cursor to second result', () => {
- // expect(multiselection.options(2).isCursored).to.be.false;
- OptionInteractor({ index: 1, cursored: true }).exists();
+ it('sets cursor to third result', () => {
+ // expect(selection.options(0).isCursored).to.be.false;
+ OptionInteractor({ index: 2, cursored: true }).exists();
});
it('sets the appropriate aria-activedescendant on the filter', () => {
- TextInput({ ariaActiveDescendent: OptionInteractor({ index: 1 }).id }).exists();
+ TextInput({ ariaActiveDescendent: OptionInteractor({ index: 2 }).id }).exists();
});
- describe('Keyboard control on filtered list: pressing "Enter"', () => {
+ describe('Keyboard control on filtered list: move cursor up', () => {
beforeEach(async () => {
- await Keyboard.enter();
+ await Keyboard.arrowUp();
+ });
+
+ it('sets cursor to second result', () => {
+ // expect(multiselection.options(2).isCursored).to.be.false;
+ OptionInteractor({ index: 1, cursored: true }).exists();
});
- it('sets the cursored option as the value', () => {
- multiselection.has({ selected: OptionInteractor({ index: 4 }).textContent });
+ it('sets the appropriate aria-activedescendant on the filter', () => {
+ TextInput({ ariaActiveDescendent: OptionInteractor({ index: 1 }).id }).exists();
+ });
+
+ describe('Keyboard control on filtered list: pressing "Enter"', () => {
+ beforeEach(async () => {
+ await Keyboard.enter();
+ });
+
+ it('sets the cursored option as the value', () => {
+ multiselection.has({ selected: OptionInteractor({ index: 4 }).textContent });
+ });
});
});
});
- });
- describe('Supplied an \'error\' prop', () => {
- beforeEach(async () => {
- await mountWithContext(
-
- );
- });
+ describe('Supplied an \'error\' prop', () => {
+ beforeEach(async () => {
+ await mountWithContext(
+
+ );
+ });
+
+ it('contains no axe - Multiselect: error validation', runAxeTest);
+
+ it('renders a validation message', () => {
+ multiselection.has({ error: 'Selection is invalid!' });
+ });
- it('contains no axe - Multiselect: error validation', runAxeTest);
+ describe('With menu open', () => {
+ beforeEach(async () => {
+ await multiselection.open();
+ });
- it('renders a validation message', () => {
- multiselection.has({ error: 'Selection is invalid!' });
+ it('renders errors in the menu', () => {
+ menu.has({ error: 'Selection is invalid!' });
+ });
+ });
});
- describe('With menu open', () => {
+ describe('Supplied an \'warning\' prop', () => {
beforeEach(async () => {
- await multiselection.open();
+ await mountWithContext(
+
+ );
});
- it('renders errors in the menu', () => {
- menu.has({ error: 'Selection is invalid!' });
+ it('renders a warning validation message', () => {
+ multiselection.has({ warning: 'You might want to choose something different!' });
+ });
+
+ describe('With menu open', () => {
+ beforeEach(async () => {
+ await multiselection.open();
+ });
+
+ it('renders warning in the menu', () => {
+ menu.has({ warning: 'You might want to choose something different!' });
+ });
});
});
});
- describe('Supplied an \'warning\' prop', () => {
+ describe('testing actions', () => {
+ let actionSelected;
+
+ const actions = [
+ {
+ onSelect: () => { actionSelected = true; },
+ render: () =>
actionItem
,
+ }
+ ];
+
beforeEach(async () => {
+ actionSelected = false;
+
await mountWithContext(
);
});
- it('renders a warning validation message', () => {
- multiselection.has({ warning: 'You might want to choose something different!' });
+ it('renders action as last option', () => {
+ menu.has({ optionCount: listOptions.length + 1 });
+ OptionInteractor('actionItem').exists();
});
- describe('With menu open', () => {
+ describe('clicking an action', () => {
beforeEach(async () => {
- await multiselection.open();
+ await multiselection.select('actionItem');
});
- it('renders warning in the menu', () => {
- menu.has({ warning: 'You might want to choose something different!' });
- });
+ it('calls the action\'s onSelect function', () => converge(() => { if (!actionSelected) throw new Error('MultiSelection - action should be executed'); }));
});
});
-});
-describe('testing actions', () => {
- let actionSelected;
-
- const actions = [
- {
- onSelect: () => { actionSelected = true; },
- render: () => actionItem
,
- }
- ];
-
- beforeEach(async () => {
- actionSelected = false;
-
- await mountWithContext(
-
- );
- });
-
- it('renders action as last option', () => {
- menu.has({ optionCount: listOptions.length + 1 });
- OptionInteractor('actionItem').exists();
- });
+ describe('asyncFiltering', () => {
+ let filtered;
- describe('clicking an action', () => {
beforeEach(async () => {
- await multiselection.select('actionItem');
+ filtered = false;
+
+ await mountWithContext(
+ { filtered = true; }}
+ />
+ );
});
- it('calls the action\'s onSelect function', () => converge(() => { if (!actionSelected) throw new Error('MultiSelection - action should be executed'); }));
- });
-});
+ describe('opening the dropdown', () => {
+ beforeEach(async () => {
+ await multiselection.open();
+ });
-describe('asyncFiltering', () => {
- let filtered;
+ it('opens the menu', expectOpenMenu);
- beforeEach(async () => {
- filtered = false;
+ it('displays loading icon (dataOptions is undefined)', () => LoadingInteractor().exists());
- await mountWithContext(
- { filtered = true; }}
- />
- );
+ it('calls the supplied filter function', () => converge(() => filtered));
+ });
});
- describe('opening the dropdown', () => {
+ describe('when the menu is open on a small screen', () => {
beforeEach(async () => {
- await multiselection.open();
+ viewport.set(300);
+ await mountWithContext(
+
+ );
+ await multiselection.toggle();
});
- it('opens the menu', expectOpenMenu);
+ afterEach(() => {
+ viewport.reset();
+ });
- it('displays loading icon (dataOptions is undefined)', () => LoadingInteractor().exists());
+ it('should focus the input', () => multiselection.is({ focused: true }));
- it('calls the supplied filter function', () => converge(() => filtered));
- });
-});
+ describe('and the menu was closed', () => {
+ beforeEach(async () => {
+ await multiselection.toggle();
+ });
-describe('when the menu is open on a small screen', () => {
- beforeEach(async () => {
- viewport.set(300);
- await mountWithContext(
-
- );
- await multiselection.toggle();
+ it('should focus the control', () => Button().is({ focused: true }));
+ });
});
- afterEach(() => {
- viewport.reset();
- });
+ describe('when a MultiSelect is set as required and placed inside a
+ );
+ await submit.click();
+ });
- describe('and the menu was closed', () => {
- beforeEach(async () => {
- await multiselection.toggle();
+ it('should focus the filter field', () => multiselection.has({ focused: true }));
});
- it('should focus the control', () => Button().is({ focused: true }));
- });
-});
+ describe('on mobile', () => {
+ beforeEach(async () => {
+ viewport.set(300);
+
+ await mountWithContext(
+
+ );
+ await submit.click();
+ });
-describe('when a MultiSelect is set as required and placed inside a
- );
- await submit.click();
+ it('should focus the control field', () => multiselection.is({ focused: true }));
});
- it('should focus the filter field', () => multiselection.has({ focused: true }));
- });
-
- describe('on mobile', () => {
- beforeEach(async () => {
- viewport.set(300);
-
- await mountWithContext(
-
- );
- await submit.click();
- });
-
- afterEach(() => {
- viewport.reset();
- });
-
- it('should focus the control field', () => multiselection.is({ focused: true }));
- });
+ );
+ });
- describe('when supplying a showLoading prop', () => {
- beforeEach(async () => {
- await mountWithContext(
-
- );
+ it('should display loading icon', () => LoadingInteractor().exists());
});
-
- it('should display loading icon', () => LoadingInteractor().exists());
});
describe('Changing the value prop outside of render', () => {
@@ -751,7 +753,7 @@ describe('when a MultiSelect is set as required and placed inside a