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

Implemented BasePicker itemLimit #2553

Merged
merged 16 commits into from
Aug 24, 2017
Merged

Implemented BasePicker itemLimit #2553

merged 16 commits into from
Aug 24, 2017

Conversation

ohritz
Copy link
Contributor

@ohritz ohritz commented Aug 19, 2017

Pull request checklist

Description of changes

  • Added a prop "itemLimit" on the basePicker
  • logic to not render the autofill "input" if the itemLimit is defined and equal to internal item count
  • Added test for previous logic.
  • Added prop to the TagPicker in the example.

@msftclas
Copy link

@ohritz,
Thanks for having already signed the Contribution License Agreement. Your agreement has not been validated yet. We will now review your pull request.
Thanks,
Microsoft Pull Request Bot

@dzearing dzearing requested a review from joschect August 20, 2017 20:13
@@ -93,6 +93,11 @@ export interface IBasePickerProps<T> extends React.Props<any> {
* @default false
Copy link
Member

@dzearing dzearing Aug 20, 2017

Choose a reason for hiding this comment

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

Please add a change request json file. Instructions:

  1. make sure you have an upstream remote to officedev url (I call mine "upstream")

npm run change -- --b upstream/master

This change is a "minor" package bump.

For comment, these show up in release notes. Please follow the convention ComponentName: change description.

BasePicker: added itemLimit property (etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahh :) sorry about that.. done that now. Thanks.

protected okToAddMore(): boolean {
const { items } = this.state;
const { itemLimit } = this.props;
if (typeof itemLimit !== 'undefined' && items.length === itemLimit) {
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; this can be shortened to itemLimit !== undefined. AFAIK you only really need to use typeof when you are checking if a global variable like document exists (to prevent throwing) or you really care about the primitive type.

Copy link
Member

Choose a reason for hiding this comment

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

Also, you can further shorten by not even comparing undefined and just returning the length comparison. There really isn't a need to check itemLimit undefined unless you expect items.length to also potentially be undefined.

return (
  items.length === itemLimit
);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

true.. will change..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

just a thought.. performance wise...
checking for undefined on itemLimit would exit early and skip checking the length of items. a micro optimization granted, but is it worth keeping in?

Copy link
Contributor Author

@ohritz ohritz Aug 21, 2017

Choose a reason for hiding this comment

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

I tried the shortest version.

return items.length < itemLimit;

and it seems to break existing functionality, when running the tests, as itemLimit is not passed in in most cases.
I think we should go with

return itemLimit === undefined || items.length < itemLimit;

so, when its undefined, its canAddMore() === true
and when its defined, items.length has to be less than itemLimit to return true..

@@ -234,6 +243,9 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<
if (newEl) {
this.focusZone.focusElement(newEl);
}
} else if (!this.okToAddMore()) {
(items[items.length - 1] as IPickerItemProps<T>).selected = true;
Copy link
Member

Choose a reason for hiding this comment

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

if items.length is 0, you would null ref here.

Copy link
Contributor Author

@ohritz ohritz Aug 21, 2017

Choose a reason for hiding this comment

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

hmm.. not so sure we would get into this branch if items.length is 0 unless itemLimit is set to 0, and then I'm not sure the set focus would be triggered..
Will write some tests and correct accordingly..

What if we log warning when usrer sets itemLimit to 0 and recommend using disabled instead? And default to 1 if it is set to 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wrote two tests and it seems like it should be ok, what do you think?

@@ -180,6 +180,15 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<
);
}

protected okToAddMore(): boolean {
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; canAddItems may be a better name. ("more" of what? "ok to" is also inconsistent)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree.

@@ -156,7 +156,7 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<
<SelectionZone selection={ this.selection } selectionMode={ SelectionMode.multiple }>
<div className={ css('ms-BasePicker-text', styles.pickerText) } role={ 'list' }>
{ this.renderItems() }
<BaseAutoFill
{ this.okToAddMore() && (<BaseAutoFill
{ ...inputProps as any }
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing I'm not quite sure about is where focus will wind up. Normally when you select something, focus is given back to the input, in this case the input won't exist any more. I think focus and selection should be given to the last item selected in the list. I think you'll need to put the check in componentwillupdate

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's what I'm trying to achieve on line 246-248, seems to me that resetFocus() handles focus being set correctly and is called from several places. you think I should put some check in in componentwillupdate as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah! I totally missed that, should be good.

@ohritz
Copy link
Contributor Author

ohritz commented Aug 22, 2017

I wrote a test to verify the last item picked was in focus when itemLimit was reached.. and discovered a flaw in my implementation.. Testing in the browser it seems that its given focus.. but pressing backspace or del at that moment causes an error, as the onBackspace method relies on the input being around.

Im wondering if I should change the logic in the onBackspace, or if its a potential rabbit hole. and go with disabling the input instead on canAddItems() === false and leaving it in the dom?

@ohritz
Copy link
Contributor Author

ohritz commented Aug 22, 2017

the test I wrote to validate that the last picked item had focus led me down a path of minor changes which are in commit cc8ee04, everything seems to work fine in the browser, but I could not get the test to function.. trying to find the focused element and comparing it to expected value does not seem to work.

seems to be something in phantomjs.
see ariya/phantomjs#11432

I think I can write a derived picker where I can pass in a render function and use it as a spy to pull out the element being set as focused?.

@dzearing dzearing merged commit cd5b68b into microsoft:master Aug 24, 2017
@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.

Pickers: Specify maximum number of items to select
4 participants