diff --git a/common/changes/office-ui-fabric-react/pickers-focusandscroll-improvements_2018-03-30-19-24.json b/common/changes/office-ui-fabric-react/pickers-focusandscroll-improvements_2018-03-30-19-24.json new file mode 100644 index 00000000000000..dc4680e3c98c1a --- /dev/null +++ b/common/changes/office-ui-fabric-react/pickers-focusandscroll-improvements_2018-03-30-19-24.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "Pickers: Fix focus problems and scroll issues", + "type": "patch" + } + ], + "packageName": "office-ui-fabric-react", + "email": "joschect@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 e67149da186f89..13cc208e177a51 100644 --- a/packages/office-ui-fabric-react/src/components/pickers/BasePicker.tsx +++ b/packages/office-ui-fabric-react/src/components/pickers/BasePicker.tsx @@ -3,7 +3,8 @@ import { BaseComponent, KeyCodes, css, - createRef + createRef, + elementContains } from '../../Utilities'; import { FocusZone, FocusZoneDirection } from '../../FocusZone'; import { Callout, DirectionalHint } from '../../Callout'; @@ -417,30 +418,38 @@ export class BasePicker> extends BaseComponent< } protected onInputFocus = (ev: React.FocusEvent): void => { - this.setState({ isFocused: true }); - this.selection.setAllSelected(false); - if (this.input.value && this.input.value.value === '' && this.props.onEmptyInputFocus) { - this.onEmptyInputFocus(); - this.setState({ - isMostRecentlyUsedVisible: true, - moreSuggestionsAvailable: false, - suggestionsVisible: true - }); - } else if (this.input.value && this.input.value.value) { - this.setState({ - isMostRecentlyUsedVisible: false, - suggestionsVisible: true - }); - } - if (this.props.inputProps && this.props.inputProps.onFocus) { - this.props.inputProps.onFocus(ev as React.FocusEvent); + // Only trigger all of the focus if this component isn't already focused. + // For example when an item is selected or removed from the selected list it should be treated + // as though the input is still focused. + if (!this.state.isFocused) { + this.setState({ isFocused: true }); + this.selection.setAllSelected(false); + if (this.input.value && this.input.value.value === '' && this.props.onEmptyInputFocus) { + this.onEmptyInputFocus(); + this.setState({ + isMostRecentlyUsedVisible: true, + moreSuggestionsAvailable: false, + suggestionsVisible: true + }); + } else if (this.input.value && this.input.value.value) { + this.setState({ + isMostRecentlyUsedVisible: false, + suggestionsVisible: true + }); + } + if (this.props.inputProps && this.props.inputProps.onFocus) { + this.props.inputProps.onFocus(ev as React.FocusEvent); + } } } protected onInputBlur = (ev: React.FocusEvent): void => { - this.setState({ isFocused: false }); - if (this.props.inputProps && this.props.inputProps.onBlur) { - this.props.inputProps.onBlur(ev as React.FocusEvent); + // Only blur if an unrelated element gets focus. Otherwise treat it as though it still has focus. + if (!elementContains(this.root.value!, ev.relatedTarget as HTMLElement)) { + this.setState({ isFocused: false }); + if (this.props.inputProps && this.props.inputProps.onBlur) { + this.props.inputProps.onBlur(ev as React.FocusEvent); + } } } diff --git a/packages/office-ui-fabric-react/src/components/pickers/Suggestions/Suggestions.tsx b/packages/office-ui-fabric-react/src/components/pickers/Suggestions/Suggestions.tsx index 94683df7cb52c6..a49a977a698487 100644 --- a/packages/office-ui-fabric-react/src/components/pickers/Suggestions/Suggestions.tsx +++ b/packages/office-ui-fabric-react/src/components/pickers/Suggestions/Suggestions.tsx @@ -68,16 +68,25 @@ export class Suggestions extends BaseComponent, ISuggest protected _searchForMoreButton = createRef(); protected _selectedElement = createRef(); private SuggestionsItemOfProperType = SuggestionsItem as new (props: ISuggestionItemProps) => SuggestionsItem; - + private activeSelectedElement: HTMLDivElement | null; constructor(suggestionsProps: ISuggestionsProps) { super(suggestionsProps); this.state = { selectedActionType: SuggestionActionType.none, }; } - - public componentDidUpdate() { + public componentDidMount() { this.scrollSelected(); + this.activeSelectedElement = this._selectedElement ? this._selectedElement.value : null; + } + public componentDidUpdate() { + // Only scroll to selected element if the selected element has changed. Otherwise do nothing. + // This prevents some odd behavior where scrolling the active element out of view and clicking on a selected element + // will trigger a focus event and not give the clicked element the click. + if (this.activeSelectedElement && this._selectedElement.value && this.activeSelectedElement !== this._selectedElement.value) { + this.scrollSelected(); + this.activeSelectedElement = this._selectedElement.value; + } } public render() {