From 5069fe3d666f6aab199dc25316887c5b1706fa23 Mon Sep 17 00:00:00 2001 From: David McNamara Date: Fri, 5 Nov 2021 17:37:24 +0000 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=90=9B=20Prevent=20arrow=20keys=20fro?= =?UTF-8?q?m=20scrolling=20the=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addon/components/listbox.hbs | 3 +++ addon/components/listbox.js | 21 +++++++++++++++++++++ addon/components/listbox/-button.hbs | 2 ++ addon/components/listbox/-options.hbs | 1 + 4 files changed, 27 insertions(+) diff --git a/addon/components/listbox.hbs b/addon/components/listbox.hbs index 8b149a2..969e4c6 100644 --- a/addon/components/listbox.hbs +++ b/addon/components/listbox.hbs @@ -18,6 +18,7 @@ unsetActiveOption=this.unsetActiveOption setSelectedOption=this.setSelectedOption handleKeyPress=this.handleKeyPress + handleKeyDown=this.handleKeyDown handleKeyUp=this.handleKeyUp openListbox=this.openListbox closeListbox=this.closeListbox @@ -28,8 +29,10 @@ guid=this.guid isOpen=this.isOpen registerButtonElement=this.registerButtonElement + unregisterButtonElement=this.unregisterButtonElement handleButtonClick=this.handleButtonClick handleKeyPress=this.handleKeyPress + handleKeyDown=this.handleKeyDown handleKeyUp=this.handleKeyUp isDisabled=this.isDisabled openListbox=this.openListbox diff --git a/addon/components/listbox.js b/addon/components/listbox.js index 5c3a5a9..75feb64 100644 --- a/addon/components/listbox.js +++ b/addon/components/listbox.js @@ -74,6 +74,22 @@ export default class ListboxComponent extends Component { return true; } + @action + handleKeyDown(event) { + if ( + [ + 'ArrowUp', + 'ArrowDown', + 'ArrowLeft', + 'ArrowRight', + 'Home', + 'End', + ].indexOf(event.key) > -1 + ) { + event.preventDefault(); + } + } + @action handleKeyUp(event) { if (event.key === 'ArrowDown') { @@ -142,6 +158,11 @@ export default class ListboxComponent extends Component { this.buttonElement = buttonElement; } + @action + unregisterButtonElement() { + this.buttonElement = undefined; + } + @action registerLabelElement(labelElement) { this.labelElement = labelElement; diff --git a/addon/components/listbox/-button.hbs b/addon/components/listbox/-button.hbs index 04486be..e485fba 100644 --- a/addon/components/listbox/-button.hbs +++ b/addon/components/listbox/-button.hbs @@ -6,11 +6,13 @@ aria-controls={{@guid}} aria-labelledby='{{@guid}}-label {{@guid}}-button' {{on 'click' @handleButtonClick}} + {{on 'keydown' @handleKeyDown}} {{on 'keyup' @handleKeyUp}} {{on 'keypress' @handleKeyPress}} aria-expanded={{unless @isDisabled (if @isOpen 'true' 'false')}} disabled={{@isDisabled}} {{did-insert @registerButtonElement}} + {{will-destroy @unregisterButtonElement}} ...attributes > {{yield}} diff --git a/addon/components/listbox/-options.hbs b/addon/components/listbox/-options.hbs index 6241f02..ebe4920 100644 --- a/addon/components/listbox/-options.hbs +++ b/addon/components/listbox/-options.hbs @@ -14,6 +14,7 @@ {{did-insert @registerOptionsElement}} {{will-destroy @unregisterOptionsElement}} {{on 'keypress' @handleKeyPress}} + {{on 'keydown' @handleKeyDown}} {{on 'keyup' @handleKeyUp}} {{headlessui-focus-trap focusTrapOptions=(hash From e5daf5b7446f24e1b7ee379b92d28d0e271931b1 Mon Sep 17 00:00:00 2001 From: David McNamara Date: Fri, 5 Nov 2021 17:39:20 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=90=9B=20Scroll=20selected=20option?= =?UTF-8?q?=20into=20view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addon/components/listbox.js | 9 +++++++++ tests/dummy/app/components/listboxes/basic.hbs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/addon/components/listbox.js b/addon/components/listbox.js index 75feb64..61c09f2 100644 --- a/addon/components/listbox.js +++ b/addon/components/listbox.js @@ -182,6 +182,8 @@ export default class ListboxComponent extends Component { if (this.args.value === optionComponent.args.value) { this.selectedOptionIndex = this.activeOptionIndex = this.optionElements.length - 1; + + this.scrollIntoView(optionElement); } } @@ -244,6 +246,13 @@ export default class ListboxComponent extends Component { } } + scrollIntoView(optionElement) { + optionElement.parentElement.scroll( + 0, + optionElement.offsetTop - optionElement.parentElement.offsetTop + ); + } + @action unregisterOptionsElement() { this.optionsElement = undefined; diff --git a/tests/dummy/app/components/listboxes/basic.hbs b/tests/dummy/app/components/listboxes/basic.hbs index 77534ed..417b14a 100644 --- a/tests/dummy/app/components/listboxes/basic.hbs +++ b/tests/dummy/app/components/listboxes/basic.hbs @@ -30,7 +30,7 @@ {{#each this.people as |person|}} From 15494c942b1ce5ea9bbaa4ca080b82b6d12cc391 Mon Sep 17 00:00:00 2001 From: David McNamara Date: Fri, 5 Nov 2021 17:39:32 +0000 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=90=9B=20Scroll=20searched=20option?= =?UTF-8?q?=20into=20view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addon/components/listbox.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/addon/components/listbox.js b/addon/components/listbox.js index 61c09f2..a823af4 100644 --- a/addon/components/listbox.js +++ b/addon/components/listbox.js @@ -313,13 +313,14 @@ export default class ListboxComponent extends Component { this.search += key.toLowerCase(); for (let i = 0; i < this.optionElements.length; i++) { + let optionElement = this.optionElements[i]; + if ( - !this.optionElements[i].hasAttribute('disabled') && - this.optionElements[i].textContent - .trim() - .toLowerCase() - .startsWith(this.search) + !optionElement.hasAttribute('disabled') && + optionElement.textContent.trim().toLowerCase().startsWith(this.search) ) { + this.scrollIntoView(optionElement); + this.activeOptionIndex = i; break; } From f20f9dde3bc521bc4feefb5ce74fb1a473a06e97 Mon Sep 17 00:00:00 2001 From: David McNamara Date: Fri, 5 Nov 2021 17:40:40 +0000 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=90=9B=20it=20should=20be=20possible?= =?UTF-8?q?=20to=20open=20a=20listbox=20without=20submitting=20the=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addon/components/listbox/-button.hbs | 1 + tests/integration/components/listbox-test.js | 29 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/addon/components/listbox/-button.hbs b/addon/components/listbox/-button.hbs index e485fba..eef1ee2 100644 --- a/addon/components/listbox/-button.hbs +++ b/addon/components/listbox/-button.hbs @@ -1,6 +1,7 @@ {{#let (element (or @as 'button')) as |Tag|}} ', function (hooks) { assertNoActiveListboxOption(); }); }); + + test('should be possible to open a listbox without submitting the form', async function (assert) { + let callCount = 0; + + this.set('onSubmit', () => { + callCount++; + }); + + await render(hbs` +
+ + Trigger + + option + + +
+ `); + assertListboxButton({ + state: ListboxState.InvisibleUnmounted, + }); + assertListbox({ + state: ListboxState.InvisibleUnmounted, + }); + await click(getListboxButton()); + assertListbox({ state: ListboxState.Visible }); + + assert.equal(callCount, 0, 'onSubmit not called'); + }); }); From c0ebe7ab9c063f55093db3b296c8f988d2072cf4 Mon Sep 17 00:00:00 2001 From: David McNamara Date: Mon, 8 Nov 2021 14:33:27 +0000 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=A7=B9=20Tidy=20up=20listbox=20bug=20?= =?UTF-8?q?fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addon/components/listbox.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/addon/components/listbox.js b/addon/components/listbox.js index a823af4..e85c1da 100644 --- a/addon/components/listbox.js +++ b/addon/components/listbox.js @@ -8,6 +8,17 @@ const ACTIVATE_NONE = 0; const ACTIVATE_FIRST = 1; const ACTIVATE_LAST = 2; +const PREVENTED_KEYDOWN_EVENTS = new Set([ + 'ArrowUp', + 'ArrowDown', + 'ArrowLeft', + 'ArrowRight', + 'PageUp', + 'PageDown', + 'Home', + 'End', +]); + export default class ListboxComponent extends Component { @tracked activeOptionIndex; activateBehaviour = ACTIVATE_NONE; @@ -76,16 +87,7 @@ export default class ListboxComponent extends Component { @action handleKeyDown(event) { - if ( - [ - 'ArrowUp', - 'ArrowDown', - 'ArrowLeft', - 'ArrowRight', - 'Home', - 'End', - ].indexOf(event.key) > -1 - ) { + if (PREVENTED_KEYDOWN_EVENTS.has(event.key)) { event.preventDefault(); } } @@ -247,6 +249,11 @@ export default class ListboxComponent extends Component { } scrollIntoView(optionElement) { + // Cannot use optionElement.scrollIntoView() here because that function + // also scrolls the *window* by some amount. Here, we don't want to + // jerk the window, we just want to make the the option element visible + // inside its container. + optionElement.parentElement.scroll( 0, optionElement.offsetTop - optionElement.parentElement.offsetTop