diff --git a/common/changes/office-ui-fabric-react/magellan-nullPickersItem_2018-03-14-23-46.json b/common/changes/office-ui-fabric-react/magellan-nullPickersItem_2018-03-14-23-46.json new file mode 100644 index 00000000000000..f84444314565c2 --- /dev/null +++ b/common/changes/office-ui-fabric-react/magellan-nullPickersItem_2018-03-14-23-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "Pickers: Allow onItemSelected to return null", + "type": "patch" + } + ], + "packageName": "office-ui-fabric-react", + "email": "law@microsoft.com" +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/pickers/BasePicker.tsx b/packages/office-ui-fabric-react/src/components/pickers/BasePicker.tsx index 5d45d58d2286d0..9351ba8ac67693 100644 --- a/packages/office-ui-fabric-react/src/components/pickers/BasePicker.tsx +++ b/packages/office-ui-fabric-react/src/components/pickers/BasePicker.tsx @@ -596,7 +596,12 @@ export class BasePicker> extends BaseComponent< @autobind protected addItem(item: T) { - const processedItem: T | PromiseLike = this.props.onItemSelected ? (this.props.onItemSelected as any)(item) : item; + const processedItem: T | PromiseLike | null = + this.props.onItemSelected ? (this.props.onItemSelected as any)(item) : item; + + if (processedItem === null) { + return; + } const processedItemObject: T = processedItem as T; const processedItemPromiseLike: PromiseLike = processedItem as PromiseLike; diff --git a/packages/office-ui-fabric-react/src/components/pickers/BasePicker.types.ts b/packages/office-ui-fabric-react/src/components/pickers/BasePicker.types.ts index de2cb6acb91cb0..50fa5c174c9a7e 100644 --- a/packages/office-ui-fabric-react/src/components/pickers/BasePicker.types.ts +++ b/packages/office-ui-fabric-react/src/components/pickers/BasePicker.types.ts @@ -119,9 +119,10 @@ export interface IBasePickerProps extends React.Props { */ removeButtonAriaLabel?: string; /** - * A callback to process a selection after the user selects something from the picker. + * A callback to process a selection after the user selects something from the picker. If the callback returns null, + * the item will not be added to the picker. */ - onItemSelected?: (selectedItem?: T) => T | PromiseLike; + onItemSelected?: (selectedItem?: T) => T | PromiseLike | null; /** * The items that the base picker should currently display as selected. If this is provided then the picker will act as a controlled component. */ diff --git a/packages/office-ui-fabric-react/src/components/pickers/examples/TagPicker.Basic.Example.tsx b/packages/office-ui-fabric-react/src/components/pickers/examples/TagPicker.Basic.Example.tsx index 798c45a35ce544..9a92e82bbd00f2 100644 --- a/packages/office-ui-fabric-react/src/components/pickers/examples/TagPicker.Basic.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/pickers/examples/TagPicker.Basic.Example.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; -import { autobind } from '../../../Utilities'; +import { + autobind, + BaseComponent +} from '../../../Utilities'; import { TagPicker } from 'office-ui-fabric-react/lib/components/pickers/TagPicker/TagPicker'; import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox'; import { ITagPickerDemoPageState } from 'office-ui-fabric-react/lib/components/pickers/examples/ITagPickerDemoPageState'; @@ -24,7 +27,9 @@ const _testTags = [ 'yellow' ].map(item => ({ key: item, name: item })); -export class TagPickerBasicExample extends React.Component<{}, ITagPickerDemoPageState> { +export class TagPickerBasicExample extends BaseComponent<{}, ITagPickerDemoPageState> { + private _picker: TagPicker; + constructor(props: {}) { super(props); this.state = { @@ -41,6 +46,7 @@ export class TagPickerBasicExample extends React.Component<{}, ITagPickerDemoPag checked={ this.state.isPickerDisabled } onChange={ this._onDisabledButtonClick } /> + Filter items in suggestions: This picker will filter added items from the search suggestions. +
+ Filter items on selected: This picker will show already-added suggestions but will not add duplicate tags. + ) => console.log('onBlur called'), + onFocus: (ev: React.FocusEvent) => console.log('onFocus called'), + 'aria-label': 'Tag Picker' + } } + /> ); } @@ -75,7 +102,21 @@ export class TagPickerBasicExample extends React.Component<{}, ITagPickerDemoPag @autobind private _onFilterChanged(filterText: string, tagList: { key: string, name: string }[]) { - return filterText ? _testTags.filter(tag => tag.name.toLowerCase().indexOf(filterText.toLowerCase()) === 0).filter(item => !this._listContainsDocument(item, tagList)) : []; + return filterText ? _testTags.filter(tag => tag.name.toLowerCase().indexOf(filterText.toLowerCase()) === 0) + .filter(tag => !this._listContainsDocument(tag, tagList)) : []; + } + + @autobind + private _onFilterChangedNoFilter(filterText: string, tagList: { key: string, name: string }[]) { + return filterText ? _testTags.filter(tag => tag.name.toLowerCase().indexOf(filterText.toLowerCase()) === 0) : []; + } + + @autobind + private _onItemSelected(item: any) { + if (this._listContainsDocument(item, this._picker.items)) { + return null; + } + return item; } private _listContainsDocument(tag: { key: string, name: string }, tagList: { key: string, name: string }[]) {