feat(combobox): option showAllOnEmpty
This commit is contained in:
parent
942ba65d8d
commit
928a673a2f
5 changed files with 153 additions and 50 deletions
5
.changeset/shaggy-pans-kiss.md
Normal file
5
.changeset/shaggy-pans-kiss.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@lion/combobox': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add a new option showAllOnEmpty which shows the full list if the input has an empty value
|
||||||
|
|
@ -160,7 +160,24 @@ export const customMatchCondition = () => html`
|
||||||
`;
|
`;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Changing defaults
|
## Options
|
||||||
|
|
||||||
|
```js preview-story
|
||||||
|
export const showAllOnEmpty = () => html`
|
||||||
|
<lion-combobox
|
||||||
|
name="combo"
|
||||||
|
label="Show all on empty"
|
||||||
|
help-text="Shows all (options) on empty (textbox has no value)"
|
||||||
|
show-all-on-empty
|
||||||
|
>
|
||||||
|
${lazyRender(
|
||||||
|
listboxData.map(entry => html` <lion-option .choiceValue="${entry}">${entry}</lion-option> `),
|
||||||
|
)}
|
||||||
|
</lion-combobox>
|
||||||
|
`;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Changing defaults
|
||||||
|
|
||||||
By default `selection-follows-focus` will be true (aligned with the
|
By default `selection-follows-focus` will be true (aligned with the
|
||||||
wai-aria examples and the natve `<datalist>`).
|
wai-aria examples and the natve `<datalist>`).
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
type: String,
|
type: String,
|
||||||
attribute: 'match-mode',
|
attribute: 'match-mode',
|
||||||
},
|
},
|
||||||
|
showAllOnEmpty: {
|
||||||
|
type: Boolean,
|
||||||
|
attribute: 'show-all-on-empty',
|
||||||
|
},
|
||||||
__shouldAutocompleteNextUpdate: Boolean,
|
__shouldAutocompleteNextUpdate: Boolean,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +69,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override FormControlMixin
|
* @enhance FormControlMixin - add slot[name=selection-display]
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
_inputGroupInputTemplate() {
|
_inputGroupInputTemplate() {
|
||||||
|
|
@ -80,7 +84,6 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
_overlayListboxTemplate() {
|
_overlayListboxTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<slot name="_overlay-shadow-outlet"></slot>
|
|
||||||
<div id="overlay-content-node-wrapper" role="dialog">
|
<div id="overlay-content-node-wrapper" role="dialog">
|
||||||
<slot name="listbox"></slot>
|
<slot name="listbox"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -121,7 +124,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
outline: none;
|
outline: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: block;
|
font-size: inherit;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0;`;
|
padding: 0;`;
|
||||||
|
|
||||||
|
|
@ -225,6 +228,12 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
*/
|
*/
|
||||||
this.matchMode = 'all';
|
this.matchMode = 'all';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When true, the listbox is open and textbox goes from a value to empty, all options are shown.
|
||||||
|
* By default, the listbox closes on empty, similar to wai-aria example and <datalist>
|
||||||
|
*/
|
||||||
|
this.showAllOnEmpty = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @configure ListboxMixin: the wai-aria pattern and <datalist> rotate
|
* @configure ListboxMixin: the wai-aria pattern and <datalist> rotate
|
||||||
*/
|
*/
|
||||||
|
|
@ -288,7 +297,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
if (changedProperties.has('opened')) {
|
if (changedProperties.has('opened')) {
|
||||||
if (this.opened) {
|
if (this.opened) {
|
||||||
// Note we always start with -1 as a 'fundament'
|
// Note we always start with -1 as a 'fundament'
|
||||||
// For [autocomplete="inline|both"] activeIndex might be changed by
|
// For [autocomplete="inline|both"] activeIndex might be changed by a match
|
||||||
this.activeIndex = -1;
|
this.activeIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -342,7 +351,6 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
_textboxOnInput(ev) {
|
_textboxOnInput(ev) {
|
||||||
// this.__cboxInputValue = /** @type {LionOption} */ (ev.target).value;
|
|
||||||
// Schedules autocompletion of options
|
// Schedules autocompletion of options
|
||||||
this.__shouldAutocompleteNextUpdate = true;
|
this.__shouldAutocompleteNextUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
@ -427,8 +435,8 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Computes whether a user intends to autofill (inline autocomplete textbox)
|
||||||
* @overridable whether a user int
|
* @overridable
|
||||||
*/
|
*/
|
||||||
_computeUserIntendsAutoFill({ prevValue, curValue }) {
|
_computeUserIntendsAutoFill({ prevValue, curValue }) {
|
||||||
const userIsAddingChars = prevValue.length < curValue.length;
|
const userIsAddingChars = prevValue.length < curValue.length;
|
||||||
|
|
@ -445,12 +453,16 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
* Matches visibility of listbox options against current ._inputNode contents
|
* Matches visibility of listbox options against current ._inputNode contents
|
||||||
*/
|
*/
|
||||||
_handleAutocompletion() {
|
_handleAutocompletion() {
|
||||||
|
// TODO: this is captured by 'noFilter'
|
||||||
|
// It should be removed and failing tests should be fixed. Currently, this line causes
|
||||||
|
// an empty box to keep showing its options when autocomplete is 'none'.
|
||||||
if (this.autocomplete === 'none') {
|
if (this.autocomplete === 'none') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const curValue = this._inputNode.value;
|
const curValue = this._inputNode.value;
|
||||||
const prevValue = this.__hasSelection ? this.__prevCboxValueNonSelected : this.__prevCboxValue;
|
const prevValue = this.__hasSelection ? this.__prevCboxValueNonSelected : this.__prevCboxValue;
|
||||||
|
const isEmpty = !curValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The filtered list of options that will match in this autocompletion cycle
|
* The filtered list of options that will match in this autocompletion cycle
|
||||||
|
|
@ -459,21 +471,26 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
const visibleOptions = [];
|
const visibleOptions = [];
|
||||||
let hasAutoFilled = false;
|
let hasAutoFilled = false;
|
||||||
const userIntendsAutoFill = this._computeUserIntendsAutoFill({ prevValue, curValue });
|
const userIntendsAutoFill = this._computeUserIntendsAutoFill({ prevValue, curValue });
|
||||||
const isAutoFillCandidate = this.autocomplete === 'both' || this.autocomplete === 'inline';
|
const isCandidate = this.autocomplete === 'both' || this.autocomplete === 'inline';
|
||||||
|
const noFilter = this.autocomplete === 'inline' || this.autocomplete === 'none';
|
||||||
|
|
||||||
/** @typedef {LionOption & { onFilterUnmatch?:function, onFilterMatch?:function }} OptionWithFilterFn */
|
/** @typedef {LionOption & { onFilterUnmatch?:function, onFilterMatch?:function }} OptionWithFilterFn */
|
||||||
this.formElements.forEach((/** @type {OptionWithFilterFn} */ option, i) => {
|
this.formElements.forEach((/** @type {OptionWithFilterFn} */ option, i) => {
|
||||||
const show = this.autocomplete === 'inline' ? true : this.matchCondition(option, curValue);
|
// [1]. Decide whether otion should be shown
|
||||||
|
let show = false;
|
||||||
|
if (isEmpty) {
|
||||||
|
show = this.showAllOnEmpty;
|
||||||
|
} else {
|
||||||
|
show = noFilter ? true : this.matchCondition(option, curValue);
|
||||||
|
}
|
||||||
|
|
||||||
// [1]. Synchronize ._inputNode value and active descendant with closest match
|
// [2]. Synchronize ._inputNode value and active descendant with closest match
|
||||||
if (isAutoFillCandidate) {
|
if (isCandidate && !hasAutoFilled && show && userIntendsAutoFill && !option.disabled) {
|
||||||
const stringValues = typeof option.choiceValue === 'string' && typeof curValue === 'string';
|
const stringValues = typeof option.choiceValue === 'string' && typeof curValue === 'string';
|
||||||
const beginsWith =
|
const beginsWith =
|
||||||
stringValues && option.choiceValue.toLowerCase().indexOf(curValue.toLowerCase()) === 0;
|
stringValues && option.choiceValue.toLowerCase().indexOf(curValue.toLowerCase()) === 0;
|
||||||
const shouldAutoFill =
|
|
||||||
beginsWith && !hasAutoFilled && show && userIntendsAutoFill && !option.disabled;
|
|
||||||
|
|
||||||
if (shouldAutoFill) {
|
if (beginsWith) {
|
||||||
const prevLen = this._inputNode.value.length;
|
const prevLen = this._inputNode.value.length;
|
||||||
this._inputNode.value = option.choiceValue;
|
this._inputNode.value = option.choiceValue;
|
||||||
this._inputNode.selectionStart = prevLen;
|
this._inputNode.selectionStart = prevLen;
|
||||||
|
|
@ -486,19 +503,13 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [2]. Cleanup previous matching states
|
// [3]. Cleanup previous matching states
|
||||||
if (option.onFilterUnmatch) {
|
if (option.onFilterUnmatch) {
|
||||||
option.onFilterUnmatch(curValue, prevValue);
|
option.onFilterUnmatch(curValue, prevValue);
|
||||||
} else {
|
} else {
|
||||||
this._onFilterUnmatch(option, curValue, prevValue);
|
this._onFilterUnmatch(option, curValue, prevValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// [3]. If ._inputNode is empty, no filtering will be applied
|
|
||||||
if (!curValue) {
|
|
||||||
visibleOptions.push(option);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [4]. Cleanup previous visibility and a11y states
|
// [4]. Cleanup previous visibility and a11y states
|
||||||
option.setAttribute('aria-hidden', 'true');
|
option.setAttribute('aria-hidden', 'true');
|
||||||
option.removeAttribute('aria-posinset');
|
option.removeAttribute('aria-posinset');
|
||||||
|
|
@ -515,28 +526,30 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// [6]. enable a11y, visibility and user interaction for visible options
|
// [6]. Enable a11y, visibility and user interaction for visible options
|
||||||
const setSize = visibleOptions.length;
|
const setSize = visibleOptions.length;
|
||||||
visibleOptions.forEach((option, idx) => {
|
visibleOptions.forEach((option, idx) => {
|
||||||
option.setAttribute('aria-posinset', `${idx + 1}`);
|
option.setAttribute('aria-posinset', `${idx + 1}`);
|
||||||
option.setAttribute('aria-setsize', `${setSize}`);
|
option.setAttribute('aria-setsize', `${setSize}`);
|
||||||
option.removeAttribute('aria-hidden');
|
option.removeAttribute('aria-hidden');
|
||||||
});
|
});
|
||||||
/** @type {number} */
|
|
||||||
|
|
||||||
|
// [7]. If no autofill took place, we are left with the previously matched option; correct this
|
||||||
|
if (!hasAutoFilled && isCandidate && !this.multipleChoice) {
|
||||||
|
// This means there is no match for checkedIndex
|
||||||
|
this.checkedIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [8]. These values will help computing autofill intentions next autocomplete cycle
|
||||||
this.__prevCboxValueNonSelected = curValue;
|
this.__prevCboxValueNonSelected = curValue;
|
||||||
// See test "computation of "user intends autofill" works correctly afer autofill"
|
// See test 'computation of "user intends autofill" works correctly afer autofill'
|
||||||
this.__prevCboxValue = this._inputNode.value;
|
this.__prevCboxValue = this._inputNode.value;
|
||||||
this.__hasSelection = hasAutoFilled;
|
this.__hasSelection = hasAutoFilled;
|
||||||
|
|
||||||
|
// [9]. Reposition overlay
|
||||||
if (this._overlayCtrl && this._overlayCtrl._popper) {
|
if (this._overlayCtrl && this._overlayCtrl._popper) {
|
||||||
this._overlayCtrl._popper.update();
|
this._overlayCtrl._popper.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAutoFilled && isAutoFillCandidate && !this.multipleChoice) {
|
|
||||||
// This means there is no match for checkedIndex
|
|
||||||
this.checkedIndex = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -585,6 +598,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @enhance ListboxMixin
|
||||||
* @param {KeyboardEvent} ev
|
* @param {KeyboardEvent} ev
|
||||||
*/
|
*/
|
||||||
_listboxOnKeyDown(ev) {
|
_listboxOnKeyDown(ev) {
|
||||||
|
|
@ -601,7 +615,6 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
if (!this.formElements[this.activeIndex]) {
|
if (!this.formElements[this.activeIndex]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// this._syncCheckedWithTextboxOnInteraction();
|
|
||||||
if (!this.multipleChoice) {
|
if (!this.multipleChoice) {
|
||||||
this.opened = false;
|
this.opened = false;
|
||||||
}
|
}
|
||||||
|
|
@ -623,7 +636,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
||||||
|
|
||||||
__setupCombobox() {
|
__setupCombobox() {
|
||||||
// With regard to accessibility: aria-expanded and -labelledby will
|
// With regard to accessibility: aria-expanded and -labelledby will
|
||||||
// be handled by OverlatMixin and FormControlMixin respectively.
|
// be handled by OverlayMixin and FormControlMixin respectively.
|
||||||
|
|
||||||
this._comboboxNode.setAttribute('role', 'combobox');
|
this._comboboxNode.setAttribute('role', 'combobox');
|
||||||
this._comboboxNode.setAttribute('aria-haspopup', 'listbox');
|
this._comboboxNode.setAttribute('aria-haspopup', 'listbox');
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,75 @@ async function fruitFixture({ autocomplete, matchMode } = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('lion-combobox', () => {
|
describe('lion-combobox', () => {
|
||||||
|
describe('Options', () => {
|
||||||
|
describe('showAllOnEmpty', () => {
|
||||||
|
it('hides options when text in input node is cleared after typing something by default', async () => {
|
||||||
|
const el = /** @type {LionCombobox} */ (await fixture(html`
|
||||||
|
<lion-combobox name="foo">
|
||||||
|
<lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
|
||||||
|
<lion-option .choiceValue="${'Chard'}">Chard</lion-option>
|
||||||
|
<lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
|
||||||
|
<lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
|
||||||
|
</lion-combobox>
|
||||||
|
`));
|
||||||
|
|
||||||
|
const options = el.formElements;
|
||||||
|
const visibleOptions = () => options.filter(o => o.getAttribute('aria-hidden') !== 'true');
|
||||||
|
|
||||||
|
async function performChecks() {
|
||||||
|
mimicUserTyping(el, 'c');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(visibleOptions().length).to.equal(4);
|
||||||
|
mimicUserTyping(el, '');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(visibleOptions().length).to.equal(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: autocomplete 'none' should have this behavior as well
|
||||||
|
// el.autocomplete = 'none';
|
||||||
|
// await performChecks();
|
||||||
|
el.autocomplete = 'list';
|
||||||
|
await performChecks();
|
||||||
|
el.autocomplete = 'inline';
|
||||||
|
await performChecks();
|
||||||
|
el.autocomplete = 'both';
|
||||||
|
await performChecks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps showing options when text in input node is cleared after typing something', async () => {
|
||||||
|
const el = /** @type {LionCombobox} */ (await fixture(html`
|
||||||
|
<lion-combobox name="foo" autocomplete="list" show-all-on-empty>
|
||||||
|
<lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
|
||||||
|
<lion-option .choiceValue="${'Chard'}">Chard</lion-option>
|
||||||
|
<lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
|
||||||
|
<lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
|
||||||
|
</lion-combobox>
|
||||||
|
`));
|
||||||
|
|
||||||
|
const options = el.formElements;
|
||||||
|
const visibleOptions = () => options.filter(o => o.getAttribute('aria-hidden') !== 'true');
|
||||||
|
|
||||||
|
async function performChecks() {
|
||||||
|
mimicUserTyping(el, 'c');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(visibleOptions().length).to.equal(4);
|
||||||
|
mimicUserTyping(el, '');
|
||||||
|
await el.updateComplete;
|
||||||
|
expect(visibleOptions().length).to.equal(options.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.autocomplete = 'none';
|
||||||
|
await performChecks();
|
||||||
|
el.autocomplete = 'list';
|
||||||
|
await performChecks();
|
||||||
|
el.autocomplete = 'inline';
|
||||||
|
await performChecks();
|
||||||
|
el.autocomplete = 'both';
|
||||||
|
await performChecks();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Structure', () => {
|
describe('Structure', () => {
|
||||||
it('has a listbox node', async () => {
|
it('has a listbox node', async () => {
|
||||||
const el = /** @type {LionCombobox} */ (await fixture(html`
|
const el = /** @type {LionCombobox} */ (await fixture(html`
|
||||||
|
|
|
||||||
|
|
@ -275,27 +275,26 @@ export function runListboxMixinSuite(customConfig = {}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('puts "aria-setsize" on all options to indicate the total amount of options', async () => {
|
it('puts "aria-setsize" on all options to indicate the total amount of options', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionListbox} */ (await fixture(html`
|
||||||
<${tag}>
|
<${tag} autocomplete="none">
|
||||||
<${optionTag} .choiceValue=${10}>Item 1</${optionTag}>
|
<${optionTag} .choiceValue=${10}>Item 1</${optionTag}>
|
||||||
<${optionTag} .choiceValue=${20}>Item 2</${optionTag}>
|
<${optionTag} .choiceValue=${20}>Item 2</${optionTag}>
|
||||||
<${optionTag} .choiceValue=${30}>Item 3</${optionTag}>
|
<${optionTag} .choiceValue=${30}>Item 3</${optionTag}>
|
||||||
</${tag}>
|
</${tag}>
|
||||||
`);
|
`));
|
||||||
const optionEls = [].slice.call(el.querySelectorAll('lion-option'));
|
el.formElements.forEach(optionEl => {
|
||||||
optionEls.forEach(optionEl => {
|
|
||||||
expect(optionEl.getAttribute('aria-setsize')).to.equal('3');
|
expect(optionEl.getAttribute('aria-setsize')).to.equal('3');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('puts "aria-posinset" on all options to indicate their position in the listbox', async () => {
|
it('puts "aria-posinset" on all options to indicate their position in the listbox', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionListbox} */ (await fixture(html`
|
||||||
<${tag}>
|
<${tag} autocomplete="none">
|
||||||
<${optionTag} .choiceValue=${10}>Item 1</${optionTag}>
|
<${optionTag} .choiceValue=${10}>Item 1</${optionTag}>
|
||||||
<${optionTag} .choiceValue=${20}>Item 2</${optionTag}>
|
<${optionTag} .choiceValue=${20}>Item 2</${optionTag}>
|
||||||
<${optionTag} .choiceValue=${30}>Item 3</${optionTag}>
|
<${optionTag} .choiceValue=${30}>Item 3</${optionTag}>
|
||||||
</${tag}>
|
</${tag}>
|
||||||
`);
|
`));
|
||||||
const optionEls = [].slice.call(el.querySelectorAll('lion-option'));
|
const optionEls = [].slice.call(el.querySelectorAll('lion-option'));
|
||||||
optionEls.forEach((oEl, i) => {
|
optionEls.forEach((oEl, i) => {
|
||||||
expect(oEl.getAttribute('aria-posinset')).to.equal(`${i + 1}`);
|
expect(oEl.getAttribute('aria-posinset')).to.equal(`${i + 1}`);
|
||||||
|
|
@ -550,13 +549,13 @@ export function runListboxMixinSuite(customConfig = {}) {
|
||||||
expect(el.activeIndex).to.equal(3);
|
expect(el.activeIndex).to.equal(3);
|
||||||
});
|
});
|
||||||
it('navigates through open lists with [ArrowDown] [ArrowUp] keys activates the option', async () => {
|
it('navigates through open lists with [ArrowDown] [ArrowUp] keys activates the option', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionListbox} */ (await fixture(html`
|
||||||
<${tag} opened has-no-default-selected>
|
<${tag} opened has-no-default-selected autocomplete="none">
|
||||||
<${optionTag} .choiceValue=${'Item 1'}>Item 1</${optionTag}>
|
<${optionTag} .choiceValue=${'Item 1'}>Item 1</${optionTag}>
|
||||||
<${optionTag} .choiceValue=${'Item 2'}>Item 2</${optionTag}>
|
<${optionTag} .choiceValue=${'Item 2'}>Item 2</${optionTag}>
|
||||||
<${optionTag} .choiceValue=${'Item 3'}>Item 3</${optionTag}>
|
<${optionTag} .choiceValue=${'Item 3'}>Item 3</${optionTag}>
|
||||||
</${tag}>
|
</${tag}>
|
||||||
`);
|
`));
|
||||||
// Normalize across listbox/select-rich/combobox
|
// Normalize across listbox/select-rich/combobox
|
||||||
el.activeIndex = 0;
|
el.activeIndex = 0;
|
||||||
// selectionFollowsFocus will be true by default on combobox (running this suite),
|
// selectionFollowsFocus will be true by default on combobox (running this suite),
|
||||||
|
|
@ -575,8 +574,8 @@ export function runListboxMixinSuite(customConfig = {}) {
|
||||||
|
|
||||||
describe('Orientation', () => {
|
describe('Orientation', () => {
|
||||||
it('has a default value of "vertical"', async () => {
|
it('has a default value of "vertical"', async () => {
|
||||||
const el = /** @type {Listbox} */ (await fixture(html`
|
const el = /** @type {LionListbox} */ (await fixture(html`
|
||||||
<${tag} opened name="foo" autocomplete="list">
|
<${tag} opened name="foo" autocomplete="none">
|
||||||
<${optionTag} .choiceValue="${'Artichoke'}">Artichoke</${optionTag}>
|
<${optionTag} .choiceValue="${'Artichoke'}">Artichoke</${optionTag}>
|
||||||
<${optionTag} .choiceValue="${'Chard'}">Chard</${optionTag}>
|
<${optionTag} .choiceValue="${'Chard'}">Chard</${optionTag}>
|
||||||
</${tag}>
|
</${tag}>
|
||||||
|
|
@ -610,8 +609,8 @@ export function runListboxMixinSuite(customConfig = {}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses [ArrowLeft] and [ArrowRight] keys when "horizontal"', async () => {
|
it('uses [ArrowLeft] and [ArrowRight] keys when "horizontal"', async () => {
|
||||||
const el = /** @type {Listbox} */ (await fixture(html`
|
const el = /** @type {LionListbox} */ (await fixture(html`
|
||||||
<${tag} opened name="foo" orientation="horizontal" autocomplete="list">
|
<${tag} opened name="foo" orientation="horizontal" autocomplete="none">
|
||||||
<${optionTag} .choiceValue="${'Artichoke'}">Artichoke</${optionTag}>
|
<${optionTag} .choiceValue="${'Artichoke'}">Artichoke</${optionTag}>
|
||||||
<${optionTag} .choiceValue="${'Chard'}">Chard</${optionTag}>
|
<${optionTag} .choiceValue="${'Chard'}">Chard</${optionTag}>
|
||||||
</${tag}>
|
</${tag}>
|
||||||
|
|
@ -755,13 +754,13 @@ export function runListboxMixinSuite(customConfig = {}) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionListbox} */ (await fixture(html`
|
||||||
<${tag} opened selection-follows-focus>
|
<${tag} opened selection-follows-focus autocomplete="none">
|
||||||
<${optionTag} .choiceValue=${10}>Item 1</${optionTag}>
|
<${optionTag} .choiceValue=${10}>Item 1</${optionTag}>
|
||||||
<${optionTag} .choiceValue=${20}>Item 2</${optionTag}>
|
<${optionTag} .choiceValue=${20}>Item 2</${optionTag}>
|
||||||
<${optionTag} .choiceValue=${30}>Item 3</${optionTag}>
|
<${optionTag} .choiceValue=${30}>Item 3</${optionTag}>
|
||||||
</${tag}>
|
</${tag}>
|
||||||
`);
|
`));
|
||||||
const options = Array.from(el.querySelectorAll('lion-option'));
|
const options = Array.from(el.querySelectorAll('lion-option'));
|
||||||
// Normalize start values between listbox, slect and combobox and test interaction below
|
// Normalize start values between listbox, slect and combobox and test interaction below
|
||||||
el.activeIndex = 0;
|
el.activeIndex = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue