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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "BasePicker: added itemLimit property, which will allow preventing adding more items than set limit.",
"type": "minor"
}
],
"packageName": "office-ui-fabric-react",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ 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.

*/
disabled?: boolean;

/**
* Restrict the amount of selectable items.
* @default undefined
*/
itemLimit?: number;
/**
* Function that specifies how arbitrary text entered into the well is handled.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,73 @@ describe('Pickers', () => {

});

it('can will not render input when items reach itemLimit', () => {
let root = document.createElement('div');
document.body.appendChild(root);
let picker: TypedBasePicker = ReactDOM.render(
<BasePickerWithType
onResolveSuggestions={ onResolveSuggestions }
onRenderItem={ (props: IPickerItemProps<{ key: string, name: string }>) => <div key={ props.item.name }>{ basicRenderer(props) }</div> }
onRenderSuggestionsItem={ basicSuggestionRenderer }
itemLimit={ 1 }
/>,
root
) as TypedBasePicker;
let input = document.querySelector('.ms-BasePicker-input') as HTMLInputElement;
input.focus();
input.value = 'bl';
ReactTestUtils.Simulate.change(input);

let suggestions = document.querySelector('.ms-Suggestions') as HTMLInputElement;
let suggestionOptions = document.querySelectorAll('.ms-Suggestions-itemButton');
ReactTestUtils.Simulate.click(suggestionOptions[0]);
expect(picker.items.length).to.be.equal(1, 'There was not only 1 item selected');
input = document.querySelector('.ms-BasePicker-input') as HTMLInputElement;
expect(input).to.be.null;

ReactDOM.unmountComponentAtNode(root);
});

it('will still render with itemLimit set to 0', () => {
let root = document.createElement('div');
document.body.appendChild(root);
let picker: TypedBasePicker = ReactDOM.render(
<BasePickerWithType
onResolveSuggestions={ onResolveSuggestions }
onRenderItem={ (props: IPickerItemProps<{ key: string, name: string }>) => <div key={ props.item.name }>{ basicRenderer(props) }</div> }
onRenderSuggestionsItem={ basicSuggestionRenderer }
itemLimit={ 0 }
/>,
root
) as TypedBasePicker;

let input = document.querySelector('.ms-BasePicker-input') as HTMLInputElement;
expect(input).to.be.null;

ReactDOM.unmountComponentAtNode(root);
});

it('can be set with selectedItems and a lower itemLimit', () => {
let root = document.createElement('div');
document.body.appendChild(root);
let picker: TypedBasePicker = ReactDOM.render(
<BasePickerWithType
selectedItems={ [{ key: '1', name: 'blue' }, { key: '2', name: 'black' }] }
onResolveSuggestions={ onResolveSuggestions }
onRenderItem={ (props: IPickerItemProps<{ key: string, name: string }>) => <div key={ props.item.name }>{ basicRenderer(props) }</div> }
onRenderSuggestionsItem={ basicSuggestionRenderer }
itemLimit={ 0 }
/>,
root
) as TypedBasePicker;

let input = document.querySelector('.ms-BasePicker-input') as HTMLInputElement;
expect(input).to.be.null;
expect(picker.items.length).to.equal(2);

ReactDOM.unmountComponentAtNode(root);
});

});

describe('TagPicker', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.canAddItems() && (<BaseAutoFill
{ ...inputProps as any }
className={ css('ms-BasePicker-input', styles.pickerInput) }
ref={ this._resolveRef('input') }
Expand All @@ -171,7 +171,7 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<
autoComplete='off'
role='combobox'
disabled={ disabled }
/>
/>) }
</div>
</SelectionZone>
</FocusZone>
Expand All @@ -180,6 +180,12 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<
);
}

protected canAddItems(): boolean {
const { items } = this.state;
const { itemLimit } = this.props;
return itemLimit === undefined || items.length < itemLimit;
}

protected renderSuggestions(): JSX.Element | null {
let TypedSuggestion = this.SuggestionOfProperType;
return this.state.suggestionsVisible ? (
Expand Down Expand Up @@ -230,10 +236,12 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<

if (items.length && index! >= 0) {
let newEl: HTMLElement = this.root.querySelectorAll('[data-selection-index]')[Math.min(index!, items.length - 1)] as HTMLElement;

if (newEl) {
this.focusZone.focusElement(newEl);
}
} else if (!this.canAddItems()) {
(items[items.length - 1] as IPickerItemProps<T>).selected = true;
this.resetFocus(items.length - 1);
} else {
this.input.focus();
}
Expand Down Expand Up @@ -387,7 +395,6 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<

@autobind
protected onKeyDown(ev: React.KeyboardEvent<HTMLElement>) {
let value = this.input.value;
switch (ev.which) {
case KeyCodes.escape:
if (this.state.suggestionsVisible) {
Expand All @@ -414,7 +421,7 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<
break;

case KeyCodes.del:
if (ev.target === this.input.inputElement && this.state.suggestionsVisible && this.suggestionStore.currentIndex !== -1) {
if (this.input && ev.target === this.input.inputElement && this.state.suggestionsVisible && this.suggestionStore.currentIndex !== -1) {
if (this.props.onRemoveSuggestion) {
(this.props.onRemoveSuggestion as any)(this.suggestionStore.currentSuggestion!.item);
}
Expand Down Expand Up @@ -552,7 +559,7 @@ export class BasePicker<T, P extends IBasePickerProps<T>> extends BaseComponent<
// This is protected because we may expect the backspace key to work differently in a different kind of picker.
// This lets the subclass override it and provide it's own onBackspace. For an example see the BasePickerListBelow
protected onBackspace(ev: React.KeyboardEvent<HTMLElement>) {
if (this.state.items.length && !this.input.isValueSelected && this.input.cursorLocation === 0) {
if (this.state.items.length && !this.input || (!this.input.isValueSelected && this.input.cursorLocation === 0)) {
if (this.selection.getSelectedCount() > 0) {
this.removeItems(this.selection.getSelection());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class TagPickerBasicExample extends React.Component<{}, ITagPickerDemoPag
noResultsFoundText: 'No Color Tags Found'
}
}
itemLimit={ 2 }
disabled={ this.state.isPickerDisabled }
inputProps={ {
onBlur: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onBlur called'),
Expand Down