Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(select): allow focusing items by typing #2907

Merged
merged 3 commits into from
Aug 8, 2017

Conversation

crisbeto
Copy link
Member

@crisbeto crisbeto commented Feb 2, 2017

Allows for users to skip to a select item by typing, similar to the native select.

Fixes #2668.

@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Feb 2, 2017
@crisbeto crisbeto force-pushed the 2668/select-typing-focus branch from 4712994 to 81c090f Compare February 4, 2017 17:00
@crisbeto crisbeto force-pushed the 2668/select-typing-focus branch from 81c090f to a7ab227 Compare March 16, 2017 21:02
@Jagpreet16
Copy link

By when can we expect this feature to be available?

@RenatoSilveiraGomes
Copy link

Can we expect this feature available on 2.0.0-beta.6?

@crisbeto crisbeto added the Accessibility This issue is related to accessibility (a11y) label May 20, 2017
@otienoanyango
Copy link

Does this mean we should be hopeful for next beta?

@Stas-tolpekin
Copy link

@kara,
When can we expect this function?

@RBroden
Copy link

RBroden commented Jun 24, 2017

I got asked to create a select like this, as well. The select they compared it to was the angularjs ui-select select with search. So far, I am using a read-only input that displays a dropdown, non-input element on click (using a directive to handle clicks outside the container). That element has a search input and list. The search input filters the data and sets the read-only input upon clicking on a list item.

@Markus-ipse
Copy link

@crisbeto Any chance you have time to rebase this PR so that it can be merged? :)

@crisbeto crisbeto force-pushed the 2668/select-typing-focus branch from a7ab227 to 425223a Compare June 28, 2017 17:33
@anthonyartese
Copy link

What is the status of this? Just encountered this issue when implementing md-select.

@mikeomeara1
Copy link

Without this functionality, md-select doesn't work how an html select is expected to and it's confusing/counter-intuitive to users (not to mention a real pain when you get more than a couple of items in there). Please please...oh please can we get some love on this one? Sounds like it's fixed and ready to go yes?

@kemmis
Copy link

kemmis commented Aug 2, 2017

Can we please please please have this feature? I agree that without this functionality it is a show-stopper from using the md-select control. What is needed in order to get this merged? Thanks!

let item = items[i];

// Note that fromCharCode returns uppercase letters.
if (items[i].getFocusableLabel!().toUpperCase().trim().startsWith(inputString)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace items[i] by item or remove the var instance.

/**
* Time in ms to wait after the last keypress to focus the active item.
*/
export const FOCUS_KEY_MANAGER_DEBOUNCE_INTERVAL = 200;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sent a question off internally to see if there's a well-defined time for this (since the WAI guide just says "rapid succession")


constructor(items: QueryList<Focusable>) {
super(items);
this._hasLabelFn = items && items.first && 'getFocusableLabel' in items.first;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the string 'getFocusableLabel' won't work for minifiers that do property renaming (e.g. Closure Compiler).

(just realized we do this in a couple of places throughout the lib; we should change those)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a while since I did this. Revisiting it, it makes sense to require the getFocusableLabel as a part of the Focusable interface.

@@ -29,3 +29,6 @@ export const TAB = 9;
export const ESCAPE = 27;
export const BACKSPACE = 8;
export const DELETE = 46;

export const INPUT_KEY_RANGE_START = 65;
export const INPUT_KEY_RANGE_END = 90;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just make these A and Z?
(I'm basing my preference on being used to https://github.com/google/closure-library/blob/master/closure/goog/events/keycodes.js#L37)

}

/** Debounces the input key events and focuses the proper item after the last keystroke. */
private _debounceInputEvent(keyCode: number): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do this with rxjs' debounce?

@crisbeto crisbeto force-pushed the 2668/select-typing-focus branch from 425223a to 9a788c3 Compare August 6, 2017 12:06
@crisbeto
Copy link
Member Author

crisbeto commented Aug 6, 2017

@jelbourn re-did this one to move all of the logic into the ListKeyManager, get it working with the latest master, address the feedback and clean it up in general. Can you take another look?

Allows for users to skip to a select item by typing, similar to the native select.

Fixes angular#2668.
@crisbeto crisbeto force-pushed the 2668/select-typing-focus branch from 9a788c3 to 72e245d Compare August 6, 2017 12:15
private _tabOut = new Subject<void>();
private _wrap: boolean = false;
private _wrap = false;
private _pressedInputKeys: number[] = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add description for _pressedInputKeys? This one isn't as immediately obvious as the other properties

this._typeaheadSubscription.unsubscribe();
}

this._typeaheadSubscription = RxChain.from(this._nonNavigationKeyStream)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add a description of how you're using _pressedInputKeys in this chain

.call(filter, () => this._pressedInputKeys.length > 0)
.call(map, () => String.fromCharCode(...this._pressedInputKeys))
.subscribe(inputString => {
const activeItemIndex = this._items.toArray().findIndex(item => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IE11 doesn't have findIndex or startsWith

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It gets added by core-js, we have a couple of other places where we use startsWith.

.call(filter, () => this._pressedInputKeys.length > 0)
.call(map, () => String.fromCharCode(...this._pressedInputKeys))
.subscribe(inputString => {
const activeItemIndex = this._items.toArray().findIndex(item => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activeItemIndex -> matchedItemIndex?

*/
export interface CanDisable {
export interface ListKeyManagerItem {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Item -> Option? I think every time we use this it's for the listbox + option combo

return;
case DOWN_ARROW: this.setNextItemActive(); break;
case UP_ARROW: this.setPreviousItemActive(); break;
default: this._nonNavigationKeyStream.next(event.keyCode); return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add back comment about using return here to skip preventDefault?

*/
export interface Focusable extends CanDisable {
export interface Focusable extends ListKeyManagerItem {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make this something like FocusableOption now?

@@ -174,6 +174,11 @@ export class MdOption {
}
}

/** Fetches the label to be used when determining whether the option should be focused. */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Fetches" -> "Gets", since "fetch" implies a server call

withTypeAhead(debounceInterval = 200): this {
if (this._items.length && this._items.some(item => typeof item.getLabel !== 'function')) {
throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test for this error?

@crisbeto crisbeto requested a review from tinayuangao as a code owner August 7, 2017 20:52
@crisbeto
Copy link
Member Author

crisbeto commented Aug 7, 2017

Addressed the feedback @jelbourn, can you take another look?

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jelbourn jelbourn added pr: lgtm action: merge The PR is ready for merge by the caretaker and removed pr: needs review labels Aug 7, 2017
@mmalerba mmalerba merged commit 5ebca5e into angular:master Aug 8, 2017
@naveedahmed1
Copy link

Thank you so much team, awesome work! It was much needed :)

crisbeto added a commit to crisbeto/material2 that referenced this pull request Aug 12, 2017
This is something that came up in angular#2907 (comment). We shouldn't be using the `if ('someString' in obj)` checks, because they can be broken by minifiers that rename class members.
andrewseguin pushed a commit that referenced this pull request Aug 15, 2017
This is something that came up in #2907 (comment). We shouldn't be using the `if ('someString' in obj)` checks, because they can be broken by minifiers that rename class members.
// and convert those letters back into a string. Afterwards find the first item that starts
// with that string and select it.
this._typeaheadSubscription = RxChain.from(this._nonNavigationKeyStream)
.call(filter, keyCode => keyCode >= A && keyCode <= Z)
Copy link

@andrewbents andrewbents Aug 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, it only searches by letters? What if options contain other characters? Also, a lot of languages have more letters, so some of them don't fit in the A-Z range

Copy link
Member Author

@crisbeto crisbeto Aug 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checks the keyboard codes, not the actual letters. E.g. 68 will correspond to d in English and д in a Bulgarian phonetic keyboard. As for the languages with more letters, nothing has come up yet, but we can always update the range. This seems to be what Closure uses as well.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Russian has 6 letters outside this range, they are on [ ] ; ' , . keys. So, they won't be handled? Also I think non-alphabetic characters should be supported as well

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about numbers? Does it work with numeric values? And when should we expect it to be release?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've submitted #6543 that should handle more or less everything. It'll still fall back to A-Z and 0-9 for browsers that don't support KeyboardEvent.key (should be a very small percentage).

crisbeto added a commit to crisbeto/material2 that referenced this pull request Aug 18, 2017
…cters

As per the discussion on angular#2907 (comment), the current typeahead setup won't cover the cases where the user is typing in an alphabet that has more characters than English. These changes update the check so that it catches any typed character (including non-alphanumeric characters).
@wags1999
Copy link

Any idea when this will be in a release? Our users are complaining, so we're considering ripping out md-select and using a different library's control... but if a release is imminent we'd just wait for that...

@naveedahmed1
Copy link

Should this work with latest release (2.0.0-beta.11)? Somehow, its still not working for me.

@PCASME
Copy link

PCASME commented Oct 2, 2017

I'm in version 2.0.0-beta.11 and does not work.

@k-krupka
Copy link

do we have a working example of this somewhere?

@mmalerba
Copy link
Contributor

@naveedahmed1
Copy link

Please also add support for:

Home key - Which should take to the first item of the list
End Key - which should take to the last item of the list

@kylekrzeski
Copy link

+1 When is this expected to be released?

@benb7760
Copy link

@kylekrzeski two months ago in 2.0.0-beta.10

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 7, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Accessibility This issue is related to accessibility (a11y) action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

When I have to open md-select and press 'A' key then move focus should be on 'A' string like 'Apple'