diff --git a/common/changes/office-ui-fabric-react/ComboBox_Add_onPendingValueSet_Callback_2018-02-27-01-11.json b/common/changes/office-ui-fabric-react/ComboBox_Add_onPendingValueSet_Callback_2018-02-27-01-11.json new file mode 100644 index 00000000000000..80e16872b9bf29 --- /dev/null +++ b/common/changes/office-ui-fabric-react/ComboBox_Add_onPendingValueSet_Callback_2018-02-27-01-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "ComboBox adding onPendingValueChanged callback prop to run when pending value is changed", + "type": "patch" + } + ], + "packageName": "office-ui-fabric-react", + "email": "madosal@microsoft.com" +} diff --git a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx index 5a7d0e8d1ae032..938e0a5c26a4ac 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -122,6 +122,8 @@ export class ComboBox extends BaseComponent { private _isScrollIdle: boolean; + private _hasPendingValue: boolean; + private readonly _scrollIdleDelay: number = 250 /* ms */; private _scrollIdleTimeoutId: number | undefined; @@ -233,6 +235,8 @@ export class ComboBox extends BaseComponent { this._select(); } + this._notifyPendingValueChanged(prevState); + if (isOpen && !prevState.isOpen && onMenuOpen) { onMenuOpen(); } @@ -688,7 +692,7 @@ export class ComboBox extends BaseComponent { * @param searchDirection - the direction to search along the options from the given index */ private _setSelectedIndex(index: number, searchDirection: SearchDirection = SearchDirection.none) { - const { onChanged } = this.props; + const { onChanged, onPendingValueChanged } = this.props; const { selectedIndex, currentOptions } = this.state; // Find the next selectable index, if searchDirection is none @@ -709,6 +713,12 @@ export class ComboBox extends BaseComponent { selectedIndex: index }); + // If ComboBox value is changed, revert preview first + if (this._hasPendingValue && onPendingValueChanged) { + onPendingValueChanged(); + this._hasPendingValue = false; + } + // Did the creator give us an onChanged callback? if (onChanged) { onChanged(option, index); @@ -1233,6 +1243,41 @@ export class ComboBox extends BaseComponent { } } + private _notifyPendingValueChanged(prevState: IComboBoxState) { + const { onPendingValueChanged } = this.props; + + if (!onPendingValueChanged) { + return; + } + + const { + currentPendingValue, + currentOptions, + currentPendingValueValidIndex, + currentPendingValueValidIndexOnHover + } = this.state; + + let newPendingIndex: number | undefined = undefined; + let newPendingValue: string | undefined = undefined; + + if (currentPendingValueValidIndexOnHover !== prevState.currentPendingValueValidIndexOnHover && this._indexWithinBounds(currentOptions, currentPendingValueValidIndexOnHover)) { + // Set new pending index if hover index was changed + newPendingIndex = currentPendingValueValidIndexOnHover; + } else if (currentPendingValueValidIndex !== prevState.currentPendingValueValidIndex && this._indexWithinBounds(currentOptions, currentPendingValueValidIndex)) { + // Set new pending index if currentPendingValueValidIndex was changed + newPendingIndex = currentPendingValueValidIndex; + } else if (currentPendingValue !== prevState.currentPendingValue && currentPendingValue !== '') { + // Set pendingValue in the case it was changed and no index was changed + newPendingValue = currentPendingValue; + } + + // Notify when there is a new pending index/value. Also, if there is a pending value, it needs to send undefined. + if (newPendingIndex !== undefined || newPendingValue || this._hasPendingValue) { + onPendingValueChanged(newPendingIndex ? currentOptions[newPendingIndex] : undefined, newPendingIndex, newPendingValue); + this._hasPendingValue = newPendingIndex !== undefined || newPendingValue !== undefined; + } + } + /** * Sets the isOpen state and updates focusInputAfterClose */ diff --git a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.types.ts b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.types.ts index d8380bf22bd0d2..4a8d53820f8b48 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.types.ts +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.types.ts @@ -48,6 +48,11 @@ export interface IComboBoxProps extends ISelectableDroppableTextProps */ onChanged?: (option?: IComboBoxOption, index?: number, value?: string) => void; + /** + * Callback issued when the user changes the pending value in ComboBox + */ + onPendingValueChanged?: (option?: IComboBoxOption, index?: number, value?: string) => void; + /** * Function that gets invoked when the ComboBox menu is launched */ diff --git a/packages/office-ui-fabric-react/src/components/ComboBox/examples/ComboBox.Basic.Example.tsx b/packages/office-ui-fabric-react/src/components/ComboBox/examples/ComboBox.Basic.Example.tsx index d1cb4b9ac5bcc9..808f65d3b456af 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/examples/ComboBox.Basic.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/examples/ComboBox.Basic.Example.tsx @@ -19,20 +19,20 @@ export class ComboBoxBasicExample extends React.Component<{}, { value?: string; }> { private _testOptions = - [{ key: 'Header', text: 'Theme Fonts', itemType: SelectableOptionMenuItemType.Header }, - { key: 'A', text: 'Arial Black' }, - { key: 'B', text: 'Times New Roman' }, - { key: 'C', text: 'Comic Sans MS' }, - { key: 'divider_2', text: '-', itemType: SelectableOptionMenuItemType.Divider }, - { key: 'Header1', text: 'Other Options', itemType: SelectableOptionMenuItemType.Header }, - { key: 'D', text: 'Option d' }, - { key: 'E', text: 'Option e' }, - { key: 'F', text: 'Option f' }, - { key: 'G', text: 'Option g' }, - { key: 'H', text: 'Option h' }, - { key: 'I', text: 'Option i' }, - { key: 'J', text: 'Option j', disabled: true }, - ]; + [{ key: 'Header', text: 'Theme Fonts', itemType: SelectableOptionMenuItemType.Header }, + { key: 'A', text: 'Arial Black' }, + { key: 'B', text: 'Times New Roman' }, + { key: 'C', text: 'Comic Sans MS' }, + { key: 'divider_2', text: '-', itemType: SelectableOptionMenuItemType.Divider }, + { key: 'Header1', text: 'Other Options', itemType: SelectableOptionMenuItemType.Header }, + { key: 'D', text: 'Option d' }, + { key: 'E', text: 'Option e' }, + { key: 'F', text: 'Option f' }, + { key: 'G', text: 'Option g' }, + { key: 'H', text: 'Option h' }, + { key: 'I', text: 'Option i' }, + { key: 'J', text: 'Option j', disabled: true }, + ]; private _fontMapping: { [key: string]: string } = { ['Arial Black']: '"Arial Black", "Arial Black_MSFontService", sans-serif', @@ -80,6 +80,7 @@ export class ComboBoxBasicExample extends React.Component<{}, { onFocus={ () => console.log('onFocus called') } onBlur={ () => console.log('onBlur called') } onMenuOpen={ () => console.log('ComboBox menu opened') } + onPendingValueChanged={ (option, pendingIndex, pendingValue) => console.log('Preview value was changed. Pending index: ' + pendingIndex + '. Pending value: ' + pendingValue) } // tslint:enable:jsx-no-lambda /> @@ -132,6 +133,7 @@ export class ComboBoxBasicExample extends React.Component<{}, { onFocus={ () => console.log('onFocus called') } onBlur={ () => console.log('onBlur called') } onMenuOpen={ () => console.log('ComboBox menu opened') } + onPendingValueChanged={ (option, pendingIndex, pendingValue) => console.log('Preview value was changed. Pending index: ' + pendingIndex + '. Pending value: ' + pendingValue) } // tslint:enable:jsx-no-lambda />