Skip to content

Conversation

@stanleyymsft
Copy link
Collaborator

@stanleyymsft stanleyymsft commented Feb 7, 2018

Pull request checklist

Description of changes

Add multiSelect capability for ComboBox

Focus areas to test

When "multiSelect" property is provided for , it allows multiple selections.
Existing single select scenarios should continue to work.

@stanleyymsft stanleyymsft changed the title Stanleyy/fillin Add multiSelect capability for ComboBox Feb 7, 2018
@stanleyymsft
Copy link
Collaborator Author

The only 1 failure in screener test is regarding PeoplePicker, however this PR only modified ComboBox.

@stanleyymsft
Copy link
Collaborator Author

Hi @betrue-final-final, thanks for approving the screener!

@dzearing
Copy link
Member

dzearing commented Feb 7, 2018

@jspurlin mind taking a look?

@jspurlin
Copy link
Contributor

jspurlin commented Feb 7, 2018

@stanleyymsft can you add a gif to the description showing the updated functionality?

Copy link
Contributor

@jspurlin jspurlin left a comment

Choose a reason for hiding this comment

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

ComboBoxes are not (and, by nature, should not be) multiselectable. Multiselect functionality fall under pickers, you should utilize a picker component to achieve this functionalty

@betrue-final-final
Copy link
Member

Jeremy, this is functionality we've had for a long time in SharePoint, and is a perfectly valid mental model. This isn't any different from selecting or creating multiple tags.

@jspurlin
Copy link
Contributor

jspurlin commented Feb 7, 2018

@betrue-final-final while the idea of a multiselectable input with a menu also (potentially) present makes sense, it doesn't make sense for a comboBox (especially when the input is editable), that functionality is more suited for a picker component. The only case that could make this be okay is when allowFreeform and autoComplete are false (hence you can't place an IP and edit the input), this could be a reason to leave the dropdown component around. I pulled down the change and played with it and there's a lot of things that need to be worked out around rendering the options in the input when multiple things are selected, as well as processing the currently pending input when other text is present... If we do the updates for making those things act reasonably, we'll be almost exactly recreating the picker functionality

@betrue-final-final
Copy link
Member

Just to be sure we're talking about the same thing, the main purpose of the editable functionality is to add new entries, not find existing ones.

@betrue-final-final
Copy link
Member

As you can see here, I am unable to add "foo" to the list...it's a different function.
image

@jspurlin
Copy link
Contributor

jspurlin commented Feb 7, 2018

No, the editable input's main purpose isn't necessarily to add new items. It can be used for autocomplete (e.g. finding an option quickly) and/or to add new items (if allowFreeform is true). A multiselectable comboBox, especially in the change that is proposed hear doesn't really make sense and feels really wonky. I feel like there's two options: 1) update the already selected items (in the input) to render like they do in the multiselect pickers so that the user can see/remove options through the input when it has focus; OR 2) add the ability for pickers to optionally be able to add new values. Option 2 feels like the much more natural progression of the two approaches. I also noticed that the PeoplePicker practically has this functionality already

@betrue-final-final
Copy link
Member

Alright, this is going to need a more in depth discussion than on Github. Let's get together with your design team and discuss.

@stanleyymsft
Copy link
Collaborator Author

stanleyymsft commented Feb 7, 2018 via email

@betrue-final-final
Copy link
Member

betrue-final-final commented Feb 7, 2018 via email

@dzearing
Copy link
Member

@stanleyymsft any update on this? I talked with Jeremy and there are a number of accessibility tweaks to make. Have you two sync'd on this? And have you shown the changes to @betrue-final-final ?

@stanleyymsft
Copy link
Collaborator Author

stanleyymsft commented Feb 21, 2018 via email

Copy link
Contributor

@jspurlin jspurlin left a comment

Choose a reason for hiding this comment

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

What's the status if this PR?

@jspurlin jspurlin dismissed their stale review March 12, 2018 17:30

Unclear what the current status is of this review

@stanleyymsft
Copy link
Collaborator Author

stanleyymsft commented Mar 12, 2018 via email

@stanleyymsft
Copy link
Collaborator Author

Will review the PR with @betrue-final-final today.

@betrue-final-final
Copy link
Member

I just checked this out and it works great. The only small thing is that space is needed for text editing and only enter works to select an item, but it's very convenient and predictable to use.

@betrue-final-final betrue-final-final requested review from betrue-final-final and removed request for betrue-final-final March 16, 2018 20:52
Copy link
Member

@betrue-final-final betrue-final-final left a comment

Choose a reason for hiding this comment

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

This works great. Enter to invoke the menu, arrow keys work, editing works and auto completes.

@stanleyymsft
Copy link
Collaborator Author

Thanks for the design review, @betrue-final-final! Looks like Travis CI has stalled for a couple hours. @jspurlin and @dzearing , would you please provide code review sign off or additional feedback?

onClick={ this._basicComboBoxOnClick }
/>

<ComboBox
Copy link
Member

Choose a reason for hiding this comment

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

minor suggestion, is it possible to create an example that shows multi-select using a controlled combo box? (just to make sure onChange has enough information.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point! Just added it right below the existing single select controlled combobox.

// Are we at a new index? If so, update the state, otherwise
// there is nothing to do
if (index !== selectedIndex) {
if (this.props.multiSelect || selectedIndices.length < 1 || (selectedIndices.length === 1 && selectedIndices[0] !== index)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is onChanged (below, line 769) only called with the newest value added?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

onChanged is called each time a checkbox toggles.

private _removeSelectedIndex(index: number) {
const selectedIndices = this.state.selectedIndices && this.state.selectedIndices.filter((idx: number) => idx !== index) || [];
this.setState({
selectedIndices: selectedIndices
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this call onChanged?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is not needed, because _setSelectedIndex() will call onChanged() CB.

Copy link
Contributor

Choose a reason for hiding this comment

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

Where is setSelectedIndex being called?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good question. I looked into further. onChanged is only triggered for mouse click, but not for ENTER key. Fixed it now.

* Keys of the selected items. If you provide this, you must maintain selection
* state by observing onChange events and passing a new value in when changed.
*/
selectedKeys?: string[] | number[];
Copy link
Contributor

@jspurlin jspurlin Mar 21, 2018

Choose a reason for hiding this comment

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

After this, ComboBox will have defaultSelectedKey, selectedKey, defaultSelectedKeys, and selectedKeys... having both *key and *keys seems weird... we should consider updating the *key props to also be string | number | string[] | number[] instead of duplicating the existing props

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point, but there are 2 potential concerns:

  1. inconsistent with exiting IDropdownProps. If we change IDropdownProps, it will cause backward compat.
  2. if we make *Key property store multiple values, it could be a bit surprising for some developers.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can't you just override the potential types inside of ComboBox.types.ts? If you do that, you won't be changing anything for dropdowns

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That solves half of problem #1. If you guys feel comfortable with the remaining half of problem #1 (i.e., inconsistent parameters semantic) and problem #2, I can make that change. My personal opinion is that the trade off might lean towards "having both *Key and *Keys". We consolidate both into one, which is great, but beyond the half of #1 and #2 above, we will also have a bunch of type checks, since *Key will be overloaded with 4 different types now. Anyway, I'll go ahead and make the change, since you own ComboBox and I'll comply with your design tradition.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

After trying, as much as I want to override the *Key props, it doesn't work. According to the following, one can only override with a narrower type so that it ensures the sub-interface is still assignable to a reference of parent interface. In our case, we are expanding instead of narrowing the type of *Key props. So I'm going to leave this PR as is. At least it is consistent with DropDown.

https://stackoverflow.com/questions/41285211/typescript-overriding-interface-property-type-defined-in-d-ts
microsoft/TypeScript#3402

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay. We should make sure to put mutuallyExclusive warnings for if you are using any of the *key props that you shouldn't also be using any of the *keys props

Copy link
Contributor

Choose a reason for hiding this comment

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

Just a thought, couldn't you update the base and then restrict the types for dropdown? The only remaining downside at that point is that you don't know if you were given an array or an individual value (which I imagine we could always may the value an array in the code to get around this)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK. I'll make that change now if that's your preference. Just wanted to make sure you saw what I pointed out 4-replies above and feel comfortable about it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just pushed changes. Please review and sign off. thx

defaultSelectedKey?: string | number | string[] | number[];

/**
* The key of the selected item. If you provide this, you must maintain selection
Copy link
Contributor

Choose a reason for hiding this comment

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

These comments should be updated

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

} else if (typeof defaultSelectedKey === 'number') {
retKeys = [defaultSelectedKey as number];
}
} else if (selectedKey) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be an if here so that we'll fix up both the defaultSelectedKey and selectedKey? I know at most one should be used, but will we get into any trobule if they both aren't arrays when we go to process them later?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To eliminate potential confusion, I added the following comment in the code. This function handles the general logic which takes precedence. Caller decides whether they pass in defaultSelectedKey or not. In the constructor, the caller does, but it doesn't when receiving new props.

/**

  • Given default selected key(s) and selected key(s), return the selected keys(s).
  • When default selected key(s) are available, they take precedence and return them instead of selected key(s).
  • @returns No matter what specific types the input parameters are, always return an array of
  • either strings or numbers instead of premitive type. This normlization makes caller's logic easier.
    */

Copy link
Contributor

@jspurlin jspurlin left a comment

Choose a reason for hiding this comment

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

Looks good, a few minor questions/comments

@jspurlin jspurlin merged commit dd4b614 into microsoft:master Mar 23, 2018
@microsoft microsoft locked as resolved and limited conversation to collaborators Aug 31, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ComboBox: Feature Request: provide multiSelect capability

4 participants