From 07b16f912bbfe42384c251c77d131f6d9e0510ef Mon Sep 17 00:00:00 2001 From: "REDMOND\\jspurlin" Date: Mon, 12 Mar 2018 16:49:18 -0700 Subject: [PATCH 01/39] Add autoexpand on touch to align with the desired behavior --- .../src/components/ComboBox/ComboBox.tsx | 15 ++++++++++++++- .../ComboBox/__snapshots__/ComboBox.test.tsx.snap | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) 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 a04e83213471e..00fbfffe2d294 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -126,6 +126,8 @@ export class ComboBox extends BaseComponent { private _scrollIdleTimeoutId: number | undefined; + private _processingTouch: boolean; + // Determines if we should be setting // focus back to the input when the menu closes. // The general rule of thumb is if the menu was launched @@ -148,6 +150,7 @@ export class ComboBox extends BaseComponent { const selectedKey = props.defaultSelectedKey !== undefined ? props.defaultSelectedKey : props.selectedKey; this._isScrollIdle = true; + this._processingTouch = false; const index: number = this._getSelectedIndex(props.options, selectedKey); @@ -319,6 +322,7 @@ export class ComboBox extends BaseComponent { onKeyDown={ this._onInputKeyDown } onKeyUp={ this._onInputKeyUp } onClick={ this._onAutofillClick } + onTouchStart={ this._onTouchStart } onInputValueChange={ this._onInputChange } aria-expanded={ isOpen } aria-autocomplete={ this._getAriaAutoCompleteValue() } @@ -1519,11 +1523,20 @@ export class ComboBox extends BaseComponent { */ @autobind private _onAutofillClick() { - if (this.props.allowFreeform) { + if (this.props.allowFreeform && !this._processingTouch) { this.focus(this.state.isOpen); } else { this._onComboBoxClick(); } + + this._processingTouch = false; + } + + @autobind + private _onTouchStart(ev: React.TouchEvent) { + ev.preventDefault(); + ev.stopPropagation(); + this._processingTouch = true; } /** diff --git a/packages/office-ui-fabric-react/src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap b/packages/office-ui-fabric-react/src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap index fb135d862487e..300ae0e3f2da5 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap @@ -145,6 +145,7 @@ exports[`ComboBox Renders ComboBox correctly 1`] = ` onInput={[Function]} onKeyDown={[Function]} onKeyUp={[Function]} + onTouchStart={[Function]} readOnly={true} role="combobox" spellCheck={false} From 3502f3a3dfb5753f1588bcb3bbb9db7c84ef9045 Mon Sep 17 00:00:00 2001 From: "REDMOND\\jspurlin" Date: Tue, 13 Mar 2018 18:37:17 -0700 Subject: [PATCH 02/39] update to use pointer events for comboBox and splitButton --- .../src/components/Button/BaseButton.tsx | 35 +++++++++++++++++-- .../src/components/ComboBox/ComboBox.tsx | 17 +++++---- .../ContextualMenu/ContextualMenu.tsx | 23 ++++++++++-- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index 7ff26e87999ad..decb5658c40f3 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -50,6 +50,7 @@ export class BaseButton extends BaseComponent ) { + if (!this._processingTouch && this.props.onClick) { + this.props.onClick(ev); + } else { + this._onMenuClick(ev); + } + } + @autobind private _onMouseDown(ev: React.MouseEvent) { if (this.props.onMouseDown) { @@ -530,7 +550,18 @@ export class BaseButton extends BaseComponent) { + private _onPointerDown(ev: PointerEvent) { + if (ev.pointerType === 'touch') { + this._processingTouch = true; + + this._async.setTimeout(() => { + this._processingTouch = false; + }, 500); + } + } + + @autobind + private _onMenuClick(ev: React.MouseEvent) { const { onMenuClick } = this.props; if (onMenuClick) { onMenuClick(ev, this); 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 00fbfffe2d294..f7c8a0b711978 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -169,6 +169,8 @@ export class ComboBox extends BaseComponent { public componentDidMount() { // hook up resolving the options if needed on focus this._events.on(this._comboBoxWrapper, 'focus', this._onResolveOptions, true); + + this._events.on(this._comboBoxWrapper, 'pointerdown', this._onPointerDown, true); } public componentWillReceiveProps(newProps: IComboBoxProps) { @@ -322,7 +324,6 @@ export class ComboBox extends BaseComponent { onKeyDown={ this._onInputKeyDown } onKeyUp={ this._onInputKeyUp } onClick={ this._onAutofillClick } - onTouchStart={ this._onTouchStart } onInputValueChange={ this._onInputChange } aria-expanded={ isOpen } aria-autocomplete={ this._getAriaAutoCompleteValue() } @@ -1528,15 +1529,17 @@ export class ComboBox extends BaseComponent { } else { this._onComboBoxClick(); } - - this._processingTouch = false; } @autobind - private _onTouchStart(ev: React.TouchEvent) { - ev.preventDefault(); - ev.stopPropagation(); - this._processingTouch = true; + private _onPointerDown(ev: PointerEvent) { + if (ev.pointerType === 'touch') { + this._processingTouch = true; + + this._async.setTimeout(() => { + this._processingTouch = false; + }, 500); + } } /** diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 486c476b21bad..2e5a252885407 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -94,6 +94,7 @@ export class ContextualMenu extends BaseComponent + elm && this._events.on(elm, 'pointerdown', this._onPointerDown, true) + } > { + this._processingTouch = false; + }, 500); + } + } + /** * Checks if the submenu should be closed */ @@ -846,6 +859,10 @@ export class ContextualMenu extends BaseComponent Date: Tue, 13 Mar 2018 19:11:17 -0700 Subject: [PATCH 03/39] update snapshot --- .../src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/office-ui-fabric-react/src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap b/packages/office-ui-fabric-react/src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap index 300ae0e3f2da5..fb135d862487e 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/ComboBox/__snapshots__/ComboBox.test.tsx.snap @@ -145,7 +145,6 @@ exports[`ComboBox Renders ComboBox correctly 1`] = ` onInput={[Function]} onKeyDown={[Function]} onKeyUp={[Function]} - onTouchStart={[Function]} readOnly={true} role="combobox" spellCheck={false} From 4f36e14de5dedbe5b0e58d05bc6189872b8942c9 Mon Sep 17 00:00:00 2001 From: "REDMOND\\jspurlin" Date: Tue, 13 Mar 2018 19:14:04 -0700 Subject: [PATCH 04/39] remove unused variable --- .../office-ui-fabric-react/src/components/Button/BaseButton.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index decb5658c40f3..20701e8636f2f 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -432,7 +432,6 @@ export class BaseButton extends BaseComponent Date: Wed, 14 Mar 2018 06:14:03 -0700 Subject: [PATCH 05/39] Add preventDefault and stopImmediatePropagation so that IE/Edge will behave the same as other browsers (especially for comboBox) --- .../src/components/Button/BaseButton.tsx | 3 +++ .../src/components/ComboBox/ComboBox.tsx | 7 +++++-- .../src/components/ContextualMenu/ContextualMenu.tsx | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index 20701e8636f2f..d7c250f30adce 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -553,6 +553,9 @@ export class BaseButton extends BaseComponent { this._processingTouch = false; }, 500); 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 f7c8a0b711978..8141ac3297048 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -1524,8 +1524,8 @@ export class ComboBox extends BaseComponent { */ @autobind private _onAutofillClick() { - if (this.props.allowFreeform && !this._processingTouch) { - this.focus(this.state.isOpen); + if (this.props.allowFreeform) { + this.focus(this.state.isOpen || this._processingTouch); } else { this._onComboBoxClick(); } @@ -1536,6 +1536,9 @@ export class ComboBox extends BaseComponent { if (ev.pointerType === 'touch') { this._processingTouch = true; + ev.preventDefault(); + ev.stopImmediatePropagation(); + this._async.setTimeout(() => { this._processingTouch = false; }, 500); diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 2e5a252885407..c1af42c8ab811 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -680,6 +680,9 @@ export class ContextualMenu extends BaseComponent { this._processingTouch = false; }, 500); From e6e0276d992193ce59f9b2c0ac184717475179f2 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 22 Mar 2018 11:46:15 -0700 Subject: [PATCH 06/39] test change --- .../src/components/ContextualMenu/ContextualMenu.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index c1af42c8ab811..0be7bf7b02f63 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -878,6 +878,9 @@ export class ContextualMenu extends BaseComponent Date: Thu, 22 Mar 2018 11:46:51 -0700 Subject: [PATCH 07/39] removed test push --- .../src/components/ContextualMenu/ContextualMenu.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 0be7bf7b02f63..c1af42c8ab811 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -878,9 +878,6 @@ export class ContextualMenu extends BaseComponent Date: Fri, 23 Mar 2018 15:33:33 -0700 Subject: [PATCH 08/39] added change file --- ...purlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/office-ui-fabric-react/jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json diff --git a/common/changes/office-ui-fabric-react/jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json b/common/changes/office-ui-fabric-react/jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json new file mode 100644 index 0000000000000..c6c2bcab8cee4 --- /dev/null +++ b/common/changes/office-ui-fabric-react/jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "Added ontouch support to expand menus for split buttons and combo boxes", + "type": "minor" + } + ], + "packageName": "office-ui-fabric-react", + "email": "chiechan@microsoft.com" +} \ No newline at end of file From c60cacaefbe5737c047cc950e7aff92c77fe7798 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Fri, 23 Mar 2018 16:18:55 -0700 Subject: [PATCH 09/39] update bundle size --- scripts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package.json b/scripts/package.json index 3f826323130d7..300638258de17 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -49,7 +49,7 @@ "bundlesize": [ { "path": "../apps/test-bundle-button/dist/main.min.js", - "maxSize": "45.5 kB" + "maxSize": "45.7 kB" } ] } \ No newline at end of file From 8b888d8efeba0926be04db02ca5cd8ba7c0bc160 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Mon, 26 Mar 2018 20:43:04 -0700 Subject: [PATCH 10/39] temp combo box changes --- .../src/components/ComboBox/ComboBox.tsx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) 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 7b85b5cd1e651..b5c1ff8ff04ae 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -174,7 +174,12 @@ export class ComboBox extends BaseComponent { // hook up resolving the options if needed on focus this._events.on(this._comboBoxWrapper.value, 'focus', this._onResolveOptions, true); - this._events.on(this._comboBoxWrapper.value, 'pointerdown', this._onPointerDown, true); + // if ('onpointerdown' in this._comboBoxWrapper.value) { + // this._events.on(this._comboBoxWrapper.value, 'pointerdown', this._onPointerDown, true); + // } else { + // this._events.on(this._comboBoxWrapper.value, 'touchstart', this._onPointerDown, true); + // } + this._events.on(this._comboBoxWrapper.value, 'touchstart', this._onTouchStart, true); } public componentWillReceiveProps(newProps: IComboBoxProps) { @@ -1661,6 +1666,21 @@ export class ComboBox extends BaseComponent { } } + private _onTouchStart = (ev: TouchEvent): void => { + this._processingTouch = true; + + ev.preventDefault(); + ev.stopImmediatePropagation(); + + if (this._comboBox.value()) { + this._comboBox.value().focus(); + } + + this._async.setTimeout(() => { + this._processingTouch = false; + }, 500); + } + private _onPointerDown = (ev: PointerEvent): void => { if (ev.pointerType === 'touch') { this._processingTouch = true; From fbb8857d91f4b5ce55b0ba47eb789c3cde7e37f1 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Fri, 30 Mar 2018 08:26:24 -0700 Subject: [PATCH 11/39] added touch start event and added test cases --- .../src/components/Button/BaseButton.tsx | 14 +++++++- .../src/components/Button/Button.test.tsx | 34 ++++++++++++++++++ .../src/components/ComboBox/ComboBox.test.tsx | 27 ++++++++++++++ .../src/components/ComboBox/ComboBox.tsx | 30 ++++++---------- .../ContextualMenu/ContextualMenu.test.tsx | 36 +++++++++++++++++++ .../ContextualMenu/ContextualMenu.tsx | 16 ++++++++- 6 files changed, 136 insertions(+), 21 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index f79355d8d51d6..786187fdf5903 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -16,6 +16,7 @@ import { ContextualMenu, IContextualMenuProps } from '../../ContextualMenu'; import { IButtonProps, IButton } from './Button.types'; import { IButtonClassNames, getBaseButtonClassNames } from './BaseButton.classNames'; import { getClassNames as getBaseSplitButtonClassNames, ISplitButtonClassNames } from './SplitButton/SplitButton.classNames'; +import { TouchEvent } from 'react'; export interface IBaseButtonProps extends IButtonProps { baseClassName?: string; @@ -194,7 +195,7 @@ export class BaseButton extends BaseComponent { + this._processingTouch = false; + }, 500); + } + } + private _onPointerDown(ev: PointerEvent) { if (ev.pointerType === 'touch') { this._processingTouch = true; diff --git a/packages/office-ui-fabric-react/src/components/Button/Button.test.tsx b/packages/office-ui-fabric-react/src/components/Button/Button.test.tsx index 9908bc87ff259..d1084698bfaed 100644 --- a/packages/office-ui-fabric-react/src/components/Button/Button.test.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/Button.test.tsx @@ -384,6 +384,40 @@ describe('Button', () => { expect(renderedDOM.getAttribute('aria-expanded')).toEqual('true'); }); + it('Touch Start on primary button of SplitButton expands menu', () => { + const button = ReactTestUtils.renderIntoDocument( + + ); + const renderedDOM = ReactDOM.findDOMNode(button as React.ReactInstance); + const primaryButtonDOM: HTMLButtonElement = renderedDOM.getElementsByTagName('button')[0] as HTMLButtonElement; + + // in a normal scenario, when we do a touchstart we would also cause a + // click event to fire. This doesn't happen in the simulator so we're + // manually adding this in. + ReactTestUtils.Simulate.touchStart(primaryButtonDOM); + ReactTestUtils.Simulate.click(primaryButtonDOM); + expect(renderedDOM.getAttribute('aria-expanded')).toEqual('true'); + }); + it('If menu trigger is disabled, pressing down does not trigger menu', () => { const button = ReactTestUtils.renderIntoDocument( { expect(returnUndefined.mock.calls.length).toBe(1); }); + it('Call onMenuOpened when touch start on the input', () => { + let comboBoxRoot; + let buttonElement; + let inputElement; + const returnUndefined = jest.fn(); + + const wrapper = mount( + ); + comboBoxRoot = wrapper.find('.ms-ComboBox'); + + inputElement = comboBoxRoot.find('input'); + + // in a normal scenario, when we do a touchstart we would also cause a + // click event to fire. This doesn't happen in the simulator so we're + // manually adding this in. + inputElement.simulate('touchstart'); + inputElement.simulate('click'); + + expect(wrapper.find('.is-open').length).toEqual(1); + }); + it('Can type a complete option with autocomplete and allowFreeform on and submit it', () => { let updatedOption; let updatedIndex; 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 b5c1ff8ff04ae..cdd2319317af4 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -31,6 +31,7 @@ import { getClassNames, getComboBoxOptionClassNames } from './ComboBox.classNames'; +import { TouchEvent, InputHTMLAttributes } from 'react'; export interface IComboBoxState { @@ -172,14 +173,9 @@ export class ComboBox extends BaseComponent { public componentDidMount() { // hook up resolving the options if needed on focus - this._events.on(this._comboBoxWrapper.value, 'focus', this._onResolveOptions, true); - - // if ('onpointerdown' in this._comboBoxWrapper.value) { - // this._events.on(this._comboBoxWrapper.value, 'pointerdown', this._onPointerDown, true); - // } else { - // this._events.on(this._comboBoxWrapper.value, 'touchstart', this._onPointerDown, true); - // } - this._events.on(this._comboBoxWrapper.value, 'touchstart', this._onTouchStart, true); + if (this._comboBoxWrapper.value && 'onpointerdown' in this._comboBoxWrapper.value) { + this._events.on(this._comboBoxWrapper.value, 'pointerdown', this._onPointerDown, true); + } } public componentWillReceiveProps(newProps: IComboBoxProps) { @@ -336,6 +332,7 @@ export class ComboBox extends BaseComponent { onKeyDown={ this._onInputKeyDown } onKeyUp={ this._onInputKeyUp } onClick={ this._onAutofillClick } + onTouchStart={ this._onTouchStart } onInputValueChange={ this._onInputChange } aria-expanded={ isOpen } aria-autocomplete={ this._getAriaAutoCompleteValue() } @@ -1666,19 +1663,14 @@ export class ComboBox extends BaseComponent { } } - private _onTouchStart = (ev: TouchEvent): void => { - this._processingTouch = true; - - ev.preventDefault(); - ev.stopImmediatePropagation(); + private _onTouchStart = (ev: TouchEvent): void => { + if (this._comboBoxWrapper.value && !('onpointerdown' in this._comboBoxWrapper)) { + this._processingTouch = true; - if (this._comboBox.value()) { - this._comboBox.value().focus(); + this._async.setTimeout(() => { + this._processingTouch = false; + }, 500); } - - this._async.setTimeout(() => { - this._processingTouch = false; - }, 500); } private _onPointerDown = (ev: PointerEvent): void => { diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.test.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.test.tsx index e4d912aa78495..10ce8db3e0988 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.test.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.test.tsx @@ -178,6 +178,42 @@ describe('ContextualMenu', () => { expect(document.querySelector('.SubMenuClass')).toBeDefined(); }); + it('opens a splitbutton submenu item on touch start', () => { + const items: IContextualMenuItem[] = [ + { + name: 'TestText 1', + key: 'TestKey1', + split: true, + onClick: () => { alert('test') }, + subMenuProps: { + items: [ + { + name: 'SubmenuText 1', + key: 'SubmenuKey1', + className: 'SubMenuClass' + } + ] + } + }, + ]; + + const contextualMenu = ReactTestUtils.renderIntoDocument( + + ); + + const menuItem = document.querySelector('button.splitPrimary-65') as HTMLButtonElement; + + // in a normal scenario, when we do a touchstart we would also cause a + // click event to fire. This doesn't happen in the simulator so we're + // manually adding this in. + ReactTestUtils.Simulate.touchStart(menuItem); + ReactTestUtils.Simulate.click(menuItem); + + expect(document.querySelector('.is-expanded')).toBeDefined(); + }); + it('sets the correct aria-owns attribute for the submenu', () => { const submenuId = 'testSubmenuId'; const items: IContextualMenuItem[] = [ diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index add455a6a1620..698d3796e6d21 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -35,6 +35,7 @@ import { VerticalDivider } from '../../Divider'; import { ContextualMenuItem } from './ContextualMenuItem'; +import { TouchEvent } from 'react'; export interface IContextualMenuState { expandedMenuItemKey?: string; @@ -593,7 +594,9 @@ export class ContextualMenu extends BaseComponent { this._splitButtonContainers.set(item.key, el); - el && this._events.on(el, 'pointerdown', this._onPointerDown, true); + if (el && 'onpointerdown' in el) { + this._events.on(el, 'pointerdown', this._onPointerDown, true); + } } } role={ 'button' } @@ -607,6 +610,7 @@ export class ContextualMenu extends BaseComponent @@ -706,6 +710,16 @@ export class ContextualMenu extends BaseComponent) { + if (this._host && !('onpointerdown' in this._host)) { + this._processingTouch = true; + + this._async.setTimeout(() => { + this._processingTouch = false; + }, 500); + } + } + private _onPointerDown(ev: PointerEvent) { if (ev.pointerType === 'touch') { this._processingTouch = true; From 4c5720e31fcb4ac08cdd6ea2c609d43e283851d9 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Mon, 2 Apr 2018 17:55:49 -0700 Subject: [PATCH 12/39] updated tests with else case --- .../src/components/Button/BaseButton.tsx | 9 ++++----- .../src/components/ComboBox/ComboBox.test.tsx | 1 - .../src/components/ComboBox/ComboBox.tsx | 3 +++ .../components/ContextualMenu/ContextualMenu.test.tsx | 2 +- .../src/components/ContextualMenu/ContextualMenu.tsx | 3 +++ 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index 786187fdf5903..44a82018e8da1 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -207,10 +207,6 @@ export class BaseButton extends BaseComponent) { if (this._isSplitButton && this._splitButtonContainer.value && !('onpointerdown' in this._splitButtonContainer.value)) { this._processingTouch = true; this._async.setTimeout(() => { this._processingTouch = false; }, 500); + } else { + ev.preventDefault(); + ev.stopPropagation(); } } diff --git a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.test.tsx b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.test.tsx index f73847dd46faa..7183b5a921b67 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.test.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.test.tsx @@ -342,7 +342,6 @@ describe('ComboBox', () => { it('Call onMenuOpened when touch start on the input', () => { let comboBoxRoot; - let buttonElement; let inputElement; const returnUndefined = jest.fn(); 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 cdd2319317af4..e8eb0a4e2dc88 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -1670,6 +1670,9 @@ export class ComboBox extends BaseComponent { this._async.setTimeout(() => { this._processingTouch = false; }, 500); + } else { + ev.preventDefault(); + ev.stopPropagation(); } } diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.test.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.test.tsx index 10ce8db3e0988..b3fe402c4cb03 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.test.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.test.tsx @@ -184,7 +184,7 @@ describe('ContextualMenu', () => { name: 'TestText 1', key: 'TestKey1', split: true, - onClick: () => { alert('test') }, + onClick: () => { alert('test'); }, subMenuProps: { items: [ { diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 698d3796e6d21..c8ac6548acde4 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -717,6 +717,9 @@ export class ContextualMenu extends BaseComponent { this._processingTouch = false; }, 500); + } else { + ev.preventDefault(); + ev.stopPropagation(); } } From b8b07c90b2e5eecf42999ebd44457fef9971a82a Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Mon, 2 Apr 2018 23:00:28 -0700 Subject: [PATCH 13/39] removed unneeded comment --- .../src/components/Button/BaseButton.tsx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index 17bbb750914c1..b0ab1d9253871 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -582,19 +582,6 @@ export class BaseButton extends BaseComponent) { - // if (this._isSplitButton && this._splitButtonContainer.value && !('onpointerdown' in this._splitButtonContainer.value)) { - // this._processingTouch = true; - - // this._async.setTimeout(() => { - // this._processingTouch = false; - // }, 500); - // } else { - // ev.preventDefault(); - // ev.stopPropagation(); - // } - // } - private _onTouchStart: (ev: TouchEvent) => void = (ev) => { if (this._isSplitButton && this._splitButtonContainer.value && !('onpointerdown' in this._splitButtonContainer.value)) { this._processingTouch = true; From fab10294b62de0b170a45b9f48c77f3eecb6c839 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Tue, 3 Apr 2018 09:15:03 -0700 Subject: [PATCH 14/39] updated change list --- .../jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/changes/office-ui-fabric-react/jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json b/common/changes/office-ui-fabric-react/jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json index c6c2bcab8cee4..a71a47280e683 100644 --- a/common/changes/office-ui-fabric-react/jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json +++ b/common/changes/office-ui-fabric-react/jspurlin-ComboBoxExpandOnTouch_2018-03-23-22-33.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "office-ui-fabric-react", - "comment": "Added ontouch support to expand menus for split buttons and combo boxes", + "comment": "SplitButton/ComboBox: added onTouch support for menu expansion.", "type": "minor" } ], From 0293d755a027bf1b22a41ea9b8db66df41c0f9a0 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Tue, 3 Apr 2018 12:46:44 -0700 Subject: [PATCH 15/39] updated code to deal with async race conditions --- .../src/components/Button/BaseButton.tsx | 25 +++++++----- .../src/components/ComboBox/ComboBox.tsx | 26 ++++++++----- .../ContextualMenu/ContextualMenu.tsx | 38 ++++++++----------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index b0ab1d9253871..1f16483732593 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -51,6 +51,7 @@ export class BaseButton extends BaseComponent) => void = (ev) => { if (this._isSplitButton && this._splitButtonContainer.value && !('onpointerdown' in this._splitButtonContainer.value)) { - this._processingTouch = true; - - this._async.setTimeout(() => { - this._processingTouch = false; - }, 500); + this._handleTouchAndPointerEvent(); } else { ev.preventDefault(); ev.stopPropagation(); @@ -597,15 +594,25 @@ export class BaseButton extends BaseComponent { - this._processingTouch = false; - }, 500); + private _handleTouchAndPointerEvent() { + // If we already have an existing timeeout from a previous touch and pointer event + // cancel that timeout so we can set a nwe one. + if (this._lastTouchTimeoutId !== undefined) { + this._async.clearTimeout(this._lastTouchTimeoutId); + this._lastTouchTimeoutId = undefined; } + this._processingTouch = true; + + this._lastTouchTimeoutId = this._async.setTimeout(() => { + this._processingTouch = false; + }, 500); } /** 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 c2861e43d2eb6..9606f7c0ff3ec 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -132,6 +132,8 @@ export class ComboBox extends BaseComponent { private _processingTouch: boolean; + private _lastTouchTimeoutId: number | undefined; + // Determines if we should be setting // focus back to the input when the menu closes. // The general rule of thumb is if the menu was launched @@ -1665,11 +1667,7 @@ export class ComboBox extends BaseComponent { private _onTouchStart: (ev: TouchEvent) => void = (ev) => { if (this._comboBoxWrapper.value && !('onpointerdown' in this._comboBoxWrapper)) { - this._processingTouch = true; - - this._async.setTimeout(() => { - this._processingTouch = false; - }, 500); + this._handleTouchAndPointerEvent(); } else { ev.preventDefault(); ev.stopPropagation(); @@ -1678,15 +1676,25 @@ export class ComboBox extends BaseComponent { private _onPointerDown = (ev: PointerEvent): void => { if (ev.pointerType === 'touch') { - this._processingTouch = true; + this._handleTouchAndPointerEvent(); ev.preventDefault(); ev.stopImmediatePropagation(); + } + } - this._async.setTimeout(() => { - this._processingTouch = false; - }, 500); + private _handleTouchAndPointerEvent() { + // If we already have an existing timeeout from a previous touch and pointer event + // cancel that timeout so we can set a nwe one. + if (this._lastTouchTimeoutId !== undefined) { + this._async.clearTimeout(this._lastTouchTimeoutId); + this._lastTouchTimeoutId = undefined; } + this._processingTouch = true; + + this._lastTouchTimeoutId = this._async.setTimeout(() => { + this._processingTouch = false; + }, 500); } /** diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 308434addce9d..a705eb4152046 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -95,6 +95,7 @@ export class ContextualMenu extends BaseComponent; @@ -712,41 +713,34 @@ export class ContextualMenu extends BaseComponent) => void = (ev) => { if (this._host && !('onpointerdown' in this._host)) { - this._processingTouch = true; - - this._async.setTimeout(() => { - this._processingTouch = false; - }, 500); + this._handleTouchAndPointerEvent(); } else { ev.preventDefault(); ev.stopPropagation(); } } - // private _onTouchStart(ev: TouchEvent) { - // if (this._host && !('onpointerdown' in this._host)) { - // this._processingTouch = true; - - // this._async.setTimeout(() => { - // this._processingTouch = false; - // }, 500); - // } else { - // ev.preventDefault(); - // ev.stopPropagation(); - // } - // } - private _onPointerDown(ev: PointerEvent) { if (ev.pointerType === 'touch') { - this._processingTouch = true; + this._handleTouchAndPointerEvent(); ev.preventDefault(); ev.stopImmediatePropagation(); + } + } - this._async.setTimeout(() => { - this._processingTouch = false; - }, 500); + private _handleTouchAndPointerEvent() { + // If we already have an existing timeeout from a previous touch and pointer event + // cancel that timeout so we can set a nwe one. + if (this._lastTouchTimeoutId !== undefined) { + this._async.clearTimeout(this._lastTouchTimeoutId); + this._lastTouchTimeoutId = undefined; } + this._processingTouch = true; + + this._lastTouchTimeoutId = this._async.setTimeout(() => { + this._processingTouch = false; + }, 500); } /** From fa7671edfdd86d892f8289dca2dc523559c9cd24 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Tue, 3 Apr 2018 18:18:54 -0700 Subject: [PATCH 16/39] removed stopping evnet handling that stops firefox from opening the game menu; added code to detect multiple clicking on touch supported devices that will cause a race condition with settimeout for processing touch; because of the way browsers handle event order in contextual menu we need to prevent actions to allow contextual menus in edge to open and close menu properly --- .../src/components/Button/BaseButton.tsx | 3 -- .../src/components/ComboBox/ComboBox.tsx | 3 -- .../ContextualMenu/ContextualMenu.tsx | 29 ++++++++++++++----- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index 1f16483732593..edcc653402b3a 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -586,9 +586,6 @@ export class BaseButton extends BaseComponent) => void = (ev) => { if (this._isSplitButton && this._splitButtonContainer.value && !('onpointerdown' in this._splitButtonContainer.value)) { this._handleTouchAndPointerEvent(); - } else { - ev.preventDefault(); - ev.stopPropagation(); } } 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 9606f7c0ff3ec..35589f70c317e 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -1668,9 +1668,6 @@ export class ComboBox extends BaseComponent { private _onTouchStart: (ev: TouchEvent) => void = (ev) => { if (this._comboBoxWrapper.value && !('onpointerdown' in this._comboBoxWrapper)) { this._handleTouchAndPointerEvent(); - } else { - ev.preventDefault(); - ev.stopPropagation(); } } diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index a705eb4152046..e36b91af0e669 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -36,6 +36,7 @@ import { } from '../../Divider'; import { ContextualMenuItem } from './ContextualMenuItem'; import { TouchEvent } from 'react'; +import { PageContent } from '../../../../example-app-base/lib-es2015/index'; export interface IContextualMenuState { expandedMenuItemKey?: string; @@ -595,7 +596,7 @@ export class ContextualMenu extends BaseComponent { this._splitButtonContainers.set(item.key, el); - if (el && 'onpointerdown' in el) { + if (el /*&& 'onpointerdown' in el*/) { this._events.on(el, 'pointerdown', this._onPointerDown, true); } } @@ -611,7 +612,7 @@ export class ContextualMenu extends BaseComponent @@ -712,15 +713,22 @@ export class ContextualMenu extends BaseComponent) => void = (ev) => { + if (this._enterTimerId !== undefined) { + this._async.clearTimeout(this._enterTimerId); + this._enterTimerId = undefined; + // return; + } if (this._host && !('onpointerdown' in this._host)) { this._handleTouchAndPointerEvent(); - } else { - ev.preventDefault(); - ev.stopPropagation(); } } private _onPointerDown(ev: PointerEvent) { + if (this._enterTimerId !== undefined) { + this._async.clearTimeout(this._enterTimerId); + this._enterTimerId = undefined; + // return; + } if (ev.pointerType === 'touch') { this._handleTouchAndPointerEvent(); @@ -799,7 +807,7 @@ export class ContextualMenu extends BaseComponent) { - if (!this._isScrollIdle) { + if (!this._isScrollIdle || this._processingTouch) { // write code to cancel the processingtouch? return; } @@ -884,7 +892,7 @@ export class ContextualMenu extends BaseComponent) { - if (item.onMouseDown) { + if (item.onMouseDown && this._enterTimerId === undefined) { item.onMouseDown(item, ev); } } @@ -896,6 +904,13 @@ export class ContextualMenu extends BaseComponent) { const splitButtonContainer = this._splitButtonContainers.get(item.key); // get the whole splitButton container to base the menu off of + + // TODO don't process anything if we're processing a mouse enter + if (this._enterTimerId !== undefined) { + this._async.clearTimeout(this._enterTimerId); + this._enterTimerId = undefined; + // return; + } this._onItemClickBase(item, ev, (splitButtonContainer ? splitButtonContainer : ev.currentTarget) as HTMLElement); } From c284b76271ba95e17b12705e3a92a76bc6ef8613 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Wed, 4 Apr 2018 13:20:13 -0700 Subject: [PATCH 17/39] cleaned up the code; made sure to cancel actions when they're finished --- .../ContextualMenu/ContextualMenu.tsx | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index e36b91af0e669..89d2e115a3386 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -596,7 +596,7 @@ export class ContextualMenu extends BaseComponent { this._splitButtonContainers.set(item.key, el); - if (el /*&& 'onpointerdown' in el*/) { + if (el && 'onpointerdown' in el) { this._events.on(el, 'pointerdown', this._onPointerDown, true); } } @@ -612,7 +612,7 @@ export class ContextualMenu extends BaseComponent @@ -713,22 +713,12 @@ export class ContextualMenu extends BaseComponent) => void = (ev) => { - if (this._enterTimerId !== undefined) { - this._async.clearTimeout(this._enterTimerId); - this._enterTimerId = undefined; - // return; - } if (this._host && !('onpointerdown' in this._host)) { this._handleTouchAndPointerEvent(); } } private _onPointerDown(ev: PointerEvent) { - if (this._enterTimerId !== undefined) { - this._async.clearTimeout(this._enterTimerId); - this._enterTimerId = undefined; - // return; - } if (ev.pointerType === 'touch') { this._handleTouchAndPointerEvent(); @@ -738,7 +728,14 @@ export class ContextualMenu extends BaseComponent { this._processingTouch = false; + this._lastTouchTimeoutId = undefined; }, 500); } @@ -807,7 +805,7 @@ export class ContextualMenu extends BaseComponent) { - if (!this._isScrollIdle || this._processingTouch) { // write code to cancel the processingtouch? + if (!this._isScrollIdle) { return; } @@ -882,11 +880,13 @@ export class ContextualMenu extends BaseComponent { this._onSubMenuDismiss(ev); targetElement.focus(); + this._enterTimerId = undefined; }, timeoutDuration); } } @@ -909,7 +909,6 @@ export class ContextualMenu extends BaseComponent Date: Wed, 4 Apr 2018 13:29:30 -0700 Subject: [PATCH 18/39] fixed minor nitpick on style --- .../src/components/Button/BaseButton.tsx | 1 + .../src/components/ComboBox/ComboBox.tsx | 1 + .../src/components/ContextualMenu/ContextualMenu.tsx | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index edcc653402b3a..47b4bfb2de56a 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -609,6 +609,7 @@ export class BaseButton extends BaseComponent { this._processingTouch = false; + this._lastTouchTimeoutId = undefined; }, 500); } 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 35589f70c317e..1445ecc03d6c2 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -1691,6 +1691,7 @@ export class ComboBox extends BaseComponent { this._lastTouchTimeoutId = this._async.setTimeout(() => { this._processingTouch = false; + this._lastTouchTimeoutId = undefined; }, 500); } diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 89d2e115a3386..9e610f3706907 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -902,10 +902,11 @@ export class ContextualMenu extends BaseComponent) { - const splitButtonContainer = this._splitButtonContainers.get(item.key); // get the whole splitButton container to base the menu off of + const splitButtonContainer = this._splitButtonContainers.get(item.key); - // TODO don't process anything if we're processing a mouse enter + // Cancel a async menu item hover timeout action from being taken and instead + // just trigger the click event instead. if (this._enterTimerId !== undefined) { this._async.clearTimeout(this._enterTimerId); this._enterTimerId = undefined; From 30a2933e4eaa038c8b41a6786672bbe78924a14a Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Wed, 4 Apr 2018 13:55:53 -0700 Subject: [PATCH 19/39] added if case to deal with touch event if there is no onclick; removed if statement --- .../office-ui-fabric-react/src/components/Button/BaseButton.tsx | 2 +- .../office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index 47b4bfb2de56a..a41bbd7b45127 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -490,7 +490,7 @@ export class BaseButton extends BaseComponent { } public componentDidMount() { - // hook up resolving the options if needed on focus if (this._comboBoxWrapper.value && 'onpointerdown' in this._comboBoxWrapper.value) { this._events.on(this._comboBoxWrapper.value, 'pointerdown', this._onPointerDown, true); } From 1fe7db7bf17c2be7107760db9477c5e700c6eaaf Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 5 Apr 2018 13:01:18 -0700 Subject: [PATCH 20/39] removed unnecessary imports to reduce file size --- .../src/components/Button/BaseButton.tsx | 9 --------- .../src/components/ComboBox/ComboBox.tsx | 3 +-- .../src/components/ContextualMenu/ContextualMenu.tsx | 2 -- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index a41bbd7b45127..c77143c340cef 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -16,7 +16,6 @@ import { ContextualMenu, IContextualMenuProps } from '../../ContextualMenu'; import { IButtonProps, IButton } from './Button.types'; import { IButtonClassNames, getBaseButtonClassNames } from './BaseButton.classNames'; import { getClassNames as getBaseSplitButtonClassNames, ISplitButtonClassNames } from './SplitButton/SplitButton.classNames'; -import { TouchEvent } from 'react'; export interface IBaseButtonProps extends IButtonProps { baseClassName?: string; @@ -532,14 +531,6 @@ export class BaseButton extends BaseComponent; } - private _onSplitButtonClick = (ev: React.MouseEvent) => { - if (!this._processingTouch && this.props.onClick) { - this.props.onClick(ev); - } else { - this._onMenuClick(ev); - } - } - private _onMouseDown = (ev: React.MouseEvent) => { if (this.props.onMouseDown) { this.props.onMouseDown(ev); 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 24b17e6c95489..63225ce718301 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -31,7 +31,6 @@ import { getClassNames, getComboBoxOptionClassNames } from './ComboBox.classNames'; -import { TouchEvent, InputHTMLAttributes } from 'react'; export interface IComboBoxState { @@ -1664,7 +1663,7 @@ export class ComboBox extends BaseComponent { } } - private _onTouchStart: (ev: TouchEvent) => void = (ev) => { + private _onTouchStart: () => void = () => { if (this._comboBoxWrapper.value && !('onpointerdown' in this._comboBoxWrapper)) { this._handleTouchAndPointerEvent(); } diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 9e610f3706907..b8e168e1704a1 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -35,8 +35,6 @@ import { VerticalDivider } from '../../Divider'; import { ContextualMenuItem } from './ContextualMenuItem'; -import { TouchEvent } from 'react'; -import { PageContent } from '../../../../example-app-base/lib-es2015/index'; export interface IContextualMenuState { expandedMenuItemKey?: string; From ad563fe708523e677a5009031b4f0cfe9edce45d Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 5 Apr 2018 13:05:27 -0700 Subject: [PATCH 21/39] fixed build problems; added constants --- .../src/components/Button/BaseButton.tsx | 5 +++-- .../src/components/ComboBox/ComboBox.tsx | 4 +++- .../src/components/ContextualMenu/ContextualMenu.tsx | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index c77143c340cef..266d0be14cded 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -51,6 +51,7 @@ export class BaseButton extends BaseComponent) => void = (ev) => { + private _onTouchStart: () => void = () => { if (this._isSplitButton && this._splitButtonContainer.value && !('onpointerdown' in this._splitButtonContainer.value)) { this._handleTouchAndPointerEvent(); } @@ -601,7 +602,7 @@ export class BaseButton extends BaseComponent { this._processingTouch = false; this._lastTouchTimeoutId = undefined; - }, 500); + }, this._touchIdleDelay); } /** 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 63225ce718301..65c60ecf26447 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -133,6 +133,8 @@ export class ComboBox extends BaseComponent { private _lastTouchTimeoutId: number | undefined; + private readonly _touchIdleDelay: number = 500; /* ms */ + // Determines if we should be setting // focus back to the input when the menu closes. // The general rule of thumb is if the menu was launched @@ -1690,7 +1692,7 @@ export class ComboBox extends BaseComponent { this._lastTouchTimeoutId = this._async.setTimeout(() => { this._processingTouch = false; this._lastTouchTimeoutId = undefined; - }, 500); + }, this._touchIdleDelay); } /** diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index b8e168e1704a1..7e60c1f553a0d 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -95,6 +95,7 @@ export class ContextualMenu extends BaseComponent; @@ -710,7 +711,7 @@ export class ContextualMenu extends BaseComponent) => void = (ev) => { + private _onTouchStart: () => void = () => { if (this._host && !('onpointerdown' in this._host)) { this._handleTouchAndPointerEvent(); } @@ -744,7 +745,7 @@ export class ContextualMenu extends BaseComponent { this._processingTouch = false; this._lastTouchTimeoutId = undefined; - }, 500); + }, this._touchIdleDelay); } /** From 1702de42e97a0a06e148f3cabcc65dfe4a6e0909 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 5 Apr 2018 13:15:42 -0700 Subject: [PATCH 22/39] changed readonly to const --- .../src/components/Button/BaseButton.tsx | 5 +++-- .../src/components/ComboBox/ComboBox.tsx | 21 +++++++++---------- .../ContextualMenu/ContextualMenu.tsx | 15 +++++++------ 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index 266d0be14cded..b9065c072382c 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -26,6 +26,8 @@ export interface IBaseButtonState { menuProps?: IContextualMenuProps | null; } +const TouchIdleDelay = 500; /* ms */ + export class BaseButton extends BaseComponent implements IButton { private get _isSplitButton(): boolean { @@ -51,7 +53,6 @@ export class BaseButton extends BaseComponent { this._processingTouch = false; this._lastTouchTimeoutId = undefined; - }, this._touchIdleDelay); + }, TouchIdleDelay); } /** 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 65c60ecf26447..e2f37a1707c0b 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -77,6 +77,13 @@ enum HoverStatus { default = -1 } +const ScrollIdleDelay: number = 250 /* ms */; +const TouchIdleDelay: number = 500; /* ms */ + +// This is used to clear any pending autocomplete +// text (used when autocomplete is true and allowFreeform is false) +const ReadOnlyPendingAutoCompleteTimeout: number = 1000 /* ms */; + @customizable('ComboBox', ['theme']) export class ComboBox extends BaseComponent { @@ -104,10 +111,6 @@ export class ComboBox extends BaseComponent { // The base id for the comboBox private _id: string; - // This is used to clear any pending autocomplete - // text (used when autocomplete is true and allowFreeform is false) - private readonly _readOnlyPendingAutoCompleteTimeout: number = 1000 /* ms */; - // After a character is inserted when autocomplete is true and // allowFreeform is false, remember the task that will clear // the pending string of characters @@ -125,16 +128,12 @@ export class ComboBox extends BaseComponent { private _hasPendingValue: boolean; - private readonly _scrollIdleDelay: number = 250 /* ms */; - private _scrollIdleTimeoutId: number | undefined; private _processingTouch: boolean; private _lastTouchTimeoutId: number | undefined; - private readonly _touchIdleDelay: number = 500; /* ms */ - // Determines if we should be setting // focus back to the input when the menu closes. // The general rule of thumb is if the menu was launched @@ -662,7 +661,7 @@ export class ComboBox extends BaseComponent { this._lastReadOnlyAutoCompleteChangeTimeoutId = this._async.setTimeout( () => { this._lastReadOnlyAutoCompleteChangeTimeoutId = undefined; }, - this._readOnlyPendingAutoCompleteTimeout + ReadOnlyPendingAutoCompleteTimeout ); return; } @@ -1145,7 +1144,7 @@ export class ComboBox extends BaseComponent { this._isScrollIdle = false; } - this._scrollIdleTimeoutId = this._async.setTimeout(() => { this._isScrollIdle = true; }, this._scrollIdleDelay); + this._scrollIdleTimeoutId = this._async.setTimeout(() => { this._isScrollIdle = true; }, ScrollIdleDelay); } /** @@ -1692,7 +1691,7 @@ export class ComboBox extends BaseComponent { this._lastTouchTimeoutId = this._async.setTimeout(() => { this._processingTouch = false; this._lastTouchTimeoutId = undefined; - }, this._touchIdleDelay); + }, TouchIdleDelay); } /** diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 7e60c1f553a0d..40e650884a11f 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -70,6 +70,11 @@ export function canAnyMenuItemsCheck(items: IContextualMenuItem[]): boolean { }); } + +const NavigationIdleDelay: number = 250 /* ms */; + +const TouchIdleDelay: number = 500; /* ms */ + @customizable('ContextualMenu', ['theme']) @withResponsiveMode export class ContextualMenu extends BaseComponent { @@ -91,11 +96,9 @@ export class ContextualMenu extends BaseComponent; @@ -745,7 +748,7 @@ export class ContextualMenu extends BaseComponent { this._processingTouch = false; this._lastTouchTimeoutId = undefined; - }, this._touchIdleDelay); + }, TouchIdleDelay); } /** @@ -800,7 +803,7 @@ export class ContextualMenu extends BaseComponent { this._isScrollIdle = true; }, this._navigationIdleDelay); + this._scrollIdleTimeoutId = this._async.setTimeout(() => { this._isScrollIdle = true; }, NavigationIdleDelay); } private _onItemMouseEnter(item: any, ev: React.MouseEvent) { @@ -855,7 +858,7 @@ export class ContextualMenu extends BaseComponent) { const targetElement = ev.currentTarget as HTMLElement; - const { subMenuHoverDelay: timeoutDuration = this._navigationIdleDelay } = this.props; + const { subMenuHoverDelay: timeoutDuration = NavigationIdleDelay } = this.props; if (item.key === this.state.expandedMenuItemKey) { return; @@ -880,7 +883,7 @@ export class ContextualMenu extends BaseComponent { this._onSubMenuDismiss(ev); From f20265a773d10b7221ce45a584961c905049b312 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 5 Apr 2018 13:22:11 -0700 Subject: [PATCH 23/39] solved build problem with string types --- .../src/components/ComboBox/ComboBox.tsx | 4 ++-- .../src/components/ContextualMenu/ContextualMenu.tsx | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) 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 e2f37a1707c0b..e342ee7a86db7 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -77,8 +77,8 @@ enum HoverStatus { default = -1 } -const ScrollIdleDelay: number = 250 /* ms */; -const TouchIdleDelay: number = 500; /* ms */ +const ScrollIdleDelay = 250 /* ms */; +const TouchIdleDelay = 500; /* ms */ // This is used to clear any pending autocomplete // text (used when autocomplete is true and allowFreeform is false) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 40e650884a11f..2bbcac11c4b1b 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -70,10 +70,8 @@ export function canAnyMenuItemsCheck(items: IContextualMenuItem[]): boolean { }); } - -const NavigationIdleDelay: number = 250 /* ms */; - -const TouchIdleDelay: number = 500; /* ms */ +const NavigationIdleDelay = 250 /* ms */; +const TouchIdleDelay = 500; /* ms */ @customizable('ContextualMenu', ['theme']) @withResponsiveMode From ec0bd5395499032426dcfe9f5b6dfb51cdbf8b75 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 5 Apr 2018 13:36:24 -0700 Subject: [PATCH 24/39] fixed missing const that's causing build break with defined type --- .../office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e342ee7a86db7..0ef11d5f1b2f4 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -82,7 +82,7 @@ const TouchIdleDelay = 500; /* ms */ // This is used to clear any pending autocomplete // text (used when autocomplete is true and allowFreeform is false) -const ReadOnlyPendingAutoCompleteTimeout: number = 1000 /* ms */; +const ReadOnlyPendingAutoCompleteTimeout = 1000 /* ms */; @customizable('ComboBox', ['theme']) export class ComboBox extends BaseComponent { From acce3420a481d92f4c0d19cfae925f6ed37ddc06 Mon Sep 17 00:00:00 2001 From: Josh Chang Date: Wed, 11 Apr 2018 11:18:43 -0700 Subject: [PATCH 25/39] added comment for time out --- .../src/components/ContextualMenu/ContextualMenu.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 60e703f4eadb3..9564dd5cc1e59 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -150,13 +150,19 @@ export class ContextualMenu extends BaseComponent { if (this._splitButtonContainers) { this._splitButtonContainers.forEach((value: HTMLDivElement) => { this._events.on(value, 'pointerdown', this._onPointerDown, true); }); } - }, 0) + }, 0); } // Invoked immediately before a component is unmounted from the DOM. From dbaa3223c400cc5573d0c2ea17b7014c7e27578d Mon Sep 17 00:00:00 2001 From: Josh Chang Date: Wed, 11 Apr 2018 11:38:52 -0700 Subject: [PATCH 26/39] increased max size limit of bundle --- scripts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package.json b/scripts/package.json index e9275d05a36bd..27bbec05401c4 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -50,7 +50,7 @@ "bundlesize": [ { "path": "../apps/test-bundle-button/dist/test-bundle-button.min.js", - "maxSize": "45.6 kB" + "maxSize": "46.0 kB" } ] } \ No newline at end of file From dd13382dd4ca0be64b99b8f5a904c1649aead969 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 12 Apr 2018 11:15:29 -0700 Subject: [PATCH 27/39] updated contextual menu to address comments --- .../src/components/ContextualMenu/ContextualMenu.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index bc1fd41096c8c..1fc80d76c47cb 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -156,7 +156,7 @@ export class ContextualMenu extends BaseComponent { + this._async.setTimeout(() => { if (this._splitButtonContainers) { this._splitButtonContainers.forEach((value: HTMLDivElement) => { this._events.on(value, 'pointerdown', this._onPointerDown, true); @@ -167,13 +167,12 @@ export class ContextualMenu extends BaseComponent this._previousActiveElement!.focus(), 0); + this._async.setTimeout(() => this._previousActiveElement!.focus(), 0); } if (this.props.onMenuDismissed) { From 089233c5f8170b7f9b75e0f2db38631dd3a6bd7f Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 12 Apr 2018 12:28:39 -0700 Subject: [PATCH 28/39] added issue regarding dynamically generating a primary action into the split button sub menu --- .../src/components/ContextualMenu/ContextualMenu.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 1fc80d76c47cb..5b9aa6db17a4f 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -679,6 +679,12 @@ export class ContextualMenu extends BaseComponent Date: Thu, 12 Apr 2018 14:36:35 -0700 Subject: [PATCH 29/39] increased the size limit --- scripts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package.json b/scripts/package.json index 27bbec05401c4..d3935006a39f0 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -50,7 +50,7 @@ "bundlesize": [ { "path": "../apps/test-bundle-button/dist/test-bundle-button.min.js", - "maxSize": "46.0 kB" + "maxSize": "46.3 kB" } ] } \ No newline at end of file From 71ade9a3268b826c52f987e51f8315a524f03179 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Thu, 12 Apr 2018 15:15:39 -0700 Subject: [PATCH 30/39] updated with better comments and grammatical fixes --- .../src/components/Button/BaseButton.tsx | 3 +++ .../src/components/ComboBox/ComboBox.tsx | 3 +++ .../ContextualMenu/ContextualMenu.tsx | 27 ++++++++++--------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx index b9065c072382c..dbf0c004b4490 100644 --- a/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx +++ b/packages/office-ui-fabric-react/src/components/Button/BaseButton.tsx @@ -197,6 +197,9 @@ export class BaseButton extends BaseComponent { } public componentDidMount() { + // For ComboBoxes, touching anywhere in the combo box should drop the dropdown, including the input element. + // This gives more hit target space for touch environments. We're setting the onpointerdown here, because React + // does not support Pointer events yet. if (this._comboBoxWrapper.value && 'onpointerdown' in this._comboBoxWrapper.value) { this._events.on(this._comboBoxWrapper.value, 'pointerdown', this._onPointerDown, true); } diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 5b9aa6db17a4f..6c86a50f8f022 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -150,16 +150,17 @@ export class ContextualMenu extends BaseComponent { if (this._splitButtonContainers) { - this._splitButtonContainers.forEach((value: HTMLDivElement) => { - this._events.on(value, 'pointerdown', this._onPointerDown, true); + this._splitButtonContainers.forEach((splitButtonContainer: HTMLDivElement) => { + if ('onpointerdown' in splitButtonContainer) { + this._events.on(splitButtonContainer, 'pointerdown', this._onPointerDown, true); + } }); } }, 0); @@ -680,11 +681,11 @@ export class ContextualMenu extends BaseComponent Date: Sun, 29 Apr 2018 23:45:46 -0700 Subject: [PATCH 31/39] fixed edge async menu close bug --- .../ContextualMenu/ContextualMenu.tsx | 16 +++-- .../ContextualMenuSplitButton.tsx | 63 +++++++++++++++++-- .../ContextualMenuSplitButton.types.ts | 7 +++ 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 7c96aac730e3b..a5c80d3bf5f8c 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -622,6 +622,7 @@ export class ContextualMenu extends BaseComponent ); } @@ -811,10 +812,8 @@ export class ContextualMenu extends BaseComponent { + if (this._enterTimerId !== undefined) { + this._async.clearTimeout(this._enterTimerId); + this._enterTimerId = undefined; + } + } + private _onItemSubMenuExpand(item: IContextualMenuItem, target: HTMLElement) { if (this.state.expandedMenuItemKey !== item.key) { diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx index 98ed1091a7567..10a1a7ddb15e6 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx @@ -19,8 +19,18 @@ import { IContextualMenuSplitButtonProps } from './ContextualMenuSplitButton.typ export interface IContextualMenuSplitButtonState { } +const TouchIdleDelay = 500; /* ms */ + export class ContextualMenuSplitButton extends BaseComponent { private _splitButton: HTMLDivElement; + private _lastTouchTimeoutId: number | undefined; + private _processingTouch: boolean; + + public componentDidMount() { + if (this._splitButton && 'onpointerdown' in this._splitButton) { + this._events.on(this._splitButton, 'pointerdown', this._onPointerDown, true); + } + } public render(): JSX.Element | null { const { @@ -51,6 +61,7 @@ export class ContextualMenuSplitButton extends BaseComponent @@ -85,7 +96,7 @@ export class ContextualMenuSplitButton extends BaseComponent + ); @@ -173,7 +184,10 @@ export class ContextualMenuSplitButton extends BaseComponent): void => { - const { item, onItemClickBase } = this.props; + const { + item, + onItemClickBase + } = this.props; if (onItemClickBase) { onItemClickBase(item, ev, (this._splitButton ? this._splitButton : ev.currentTarget) as HTMLElement); } @@ -182,13 +196,18 @@ export class ContextualMenuSplitButton extends BaseComponent | React.KeyboardEvent): void => { const { item, - executeItemClick + executeItemClick, + onItemClick } = this.props; if (item.disabled || item.isDisabled) { return; } + if (this._processingTouch && onItemClick) { + return onItemClick(item, ev); + } + if (executeItemClick) { executeItemClick(item, ev); } @@ -204,4 +223,38 @@ export class ContextualMenuSplitButton extends BaseComponent { + if (this._splitButton && !('onpointerdown' in this._splitButton)) { + this._handleTouchAndPointerEvent(); + } + } + + private _onPointerDown = (ev: PointerEvent): void => { + if (ev.pointerType === 'touch') { + this._handleTouchAndPointerEvent(); + ev.preventDefault(); + ev.stopImmediatePropagation(); + } + } + + private _handleTouchAndPointerEvent() { + // Hack to get around the event ordering of Edge/IE where onMouseEnter gets called + // between onPointer but onClick events as opposed to Chrome/Firefox. This different ordering causes + // submenus to open and then close on touch + if (this.props.cancelExistingTimers) { + this.props.cancelExistingTimers(); + } + // If we already have an existing timeout from a previous touch/pointer event + // cancel that timeout so we can set a new one. + if (this._lastTouchTimeoutId) { + this._async.clearTimeout(this._lastTouchTimeoutId); + this._lastTouchTimeoutId = undefined; + } + this._processingTouch = true; + this._lastTouchTimeoutId = this._async.setTimeout(() => { + this._processingTouch = false; + this._lastTouchTimeoutId = undefined; + }, TouchIdleDelay); + } +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.types.ts b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.types.ts index 1e7ebdb801f2c..7ece824198bb2 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.types.ts +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.types.ts @@ -91,4 +91,11 @@ export interface IContextualMenuSplitButtonProps extends React.Props) => void; + + /** + * Hack to get around the event ordering of Edge/IE where onMouseEnter gets called + * before onPointer/click events as opposed to Chrome/Firefox causing different behaviors + * of submenu opening and then closing on touch + */ + cancelExistingTimers?: () => void; } \ No newline at end of file From cec47d07dae19e6d13936a10884da9d0b3a63f5b Mon Sep 17 00:00:00 2001 From: Josh Chang Date: Mon, 30 Apr 2018 00:08:22 -0700 Subject: [PATCH 32/39] update test --- .../__snapshots__/ContextualMenuSplitButton.test.tsx.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/__snapshots__/ContextualMenuSplitButton.test.tsx.snap b/packages/office-ui-fabric-react/src/components/ContextualMenu/__snapshots__/ContextualMenuSplitButton.test.tsx.snap index 5fe08a6dd930e..df9086fd9827b 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/__snapshots__/ContextualMenuSplitButton.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/__snapshots__/ContextualMenuSplitButton.test.tsx.snap @@ -152,6 +152,7 @@ ShallowWrapper { "onMouseEnter": [Function], "onMouseLeave": undefined, "onMouseMove": [Function], + "onTouchStart": [Function], "role": "button", "tabIndex": 0, }, @@ -466,6 +467,7 @@ ShallowWrapper { "onMouseEnter": [Function], "onMouseLeave": undefined, "onMouseMove": [Function], + "onTouchStart": [Function], "role": "button", "tabIndex": 0, }, From 6d3810eb2875530a3c143c6edbfd7b74e566d238 Mon Sep 17 00:00:00 2001 From: Josh Chang Date: Mon, 30 Apr 2018 08:03:55 -0700 Subject: [PATCH 33/39] bumped bundle size --- scripts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package.json b/scripts/package.json index a7bd473e66e63..b4c367f9708a1 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -50,7 +50,7 @@ "bundlesize": [ { "path": "../apps/test-bundle-button/dist/test-bundle-button.min.js", - "maxSize": "47.6 kB" + "maxSize": "48 kB" } ] } \ No newline at end of file From 1f9fd864d81dbe8707ed83f441c51ac261e80dfd Mon Sep 17 00:00:00 2001 From: Josh Chang Date: Mon, 30 Apr 2018 09:32:35 -0700 Subject: [PATCH 34/39] fixed gammar and moved some code around --- .../src/components/ComboBox/ComboBox.tsx | 17 +++++++++-------- .../ContextualMenuSplitButton.tsx | 4 ++-- .../ContextualMenuSplitButton.types.ts | 6 ++++-- 3 files changed, 15 insertions(+), 12 deletions(-) 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 0c5df529a4b30..0ccad75a765d6 100644 --- a/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx +++ b/packages/office-ui-fabric-react/src/components/ComboBox/ComboBox.tsx @@ -194,14 +194,15 @@ export class ComboBox extends BaseComponent { } public componentDidMount(): void { - // TODO what is current - // hook up resolving the options if needed on focus - this._events.on(this._comboBoxWrapper.current, 'focus', this._onResolveOptions, true); - // For ComboBoxes, touching anywhere in the combo box should drop the dropdown, including the input element. - // This gives more hit target space for touch environments. We're setting the onpointerdown here, because React - // does not support Pointer events yet. - if (this._comboBoxWrapper.current && 'onpointerdown' in this._comboBoxWrapper.current) { - this._events.on(this._comboBoxWrapper.value, 'pointerdown', this._onPointerDown, true); + if (this._comboBoxWrapper.current) { + // hook up resolving the options if needed on focus + this._events.on(this._comboBoxWrapper.current, 'focus', this._onResolveOptions, true); + if ('onpointerdown' in this._comboBoxWrapper.current) { + // For ComboBoxes, touching anywhere in the combo box should drop the dropdown, including the input element. + // This gives more hit target space for touch environments. We're setting the onpointerdown here, because React + // does not support Pointer events yet. + this._events.on(this._comboBoxWrapper.value, 'pointerdown', this._onPointerDown, true); + } } } diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx index 10a1a7ddb15e6..6b9128b11644d 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx @@ -240,8 +240,8 @@ export class ContextualMenuSplitButton extends BaseComponent void; } \ No newline at end of file From 8ce9bc7a1f278aadc6095a65677bca5ba01f4b29 Mon Sep 17 00:00:00 2001 From: Josh Chang Date: Mon, 30 Apr 2018 14:10:07 -0700 Subject: [PATCH 35/39] made better callback name --- .../src/components/ContextualMenu/ContextualMenu.tsx | 6 +++--- .../ContextualMenu/ContextualMenuSplitButton.tsx | 11 ++++++----- .../ContextualMenu/ContextualMenuSplitButton.types.ts | 8 ++------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index a5c80d3bf5f8c..8a17b31b25f08 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -622,7 +622,7 @@ export class ContextualMenu extends BaseComponent ); } @@ -812,7 +812,7 @@ export class ContextualMenu extends BaseComponent { + private _cancelSubMenuTimer = () => { if (this._enterTimerId !== undefined) { this._async.clearTimeout(this._enterTimerId); this._enterTimerId = undefined; diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx index 6b9128b11644d..d34cee430dd5e 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx @@ -239,11 +239,12 @@ export class ContextualMenuSplitButton extends BaseComponent) => void; /** - * Hack to get around the event ordering of Edge/IE where onMouseEnter gets called - * between onPointer and onClick events as opposed to Chrome/Firefox. This different ordering causes - * submenus to open and then close on touch - * - * Callback to cancel any existing timers for touch/pointer events + * Callback for touch/pointer events on the split button. */ - cancelExistingTimers?: () => void; + onTap?: () => void; } \ No newline at end of file From 9b45fef1ccd3f68dd568edeb5f53cc9eb4330422 Mon Sep 17 00:00:00 2001 From: Josh Chang Date: Mon, 30 Apr 2018 14:30:52 -0700 Subject: [PATCH 36/39] added pointer and touch event --- .../src/components/ContextualMenu/ContextualMenu.tsx | 6 +++++- .../ContextualMenu/ContextualMenuSplitButton.tsx | 10 +++++----- .../ContextualMenu/ContextualMenuSplitButton.types.ts | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 8a17b31b25f08..6585356053986 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -622,7 +622,7 @@ export class ContextualMenu extends BaseComponent ); } @@ -974,4 +974,8 @@ export class ContextualMenu extends BaseComponent | PointerEvent) => { + this._cancelSubMenuTimer() + } } diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx index d34cee430dd5e..f95790390eff9 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.tsx @@ -224,27 +224,27 @@ export class ContextualMenuSplitButton extends BaseComponent { + private _onTouchStart = (ev: React.TouchEvent): void => { if (this._splitButton && !('onpointerdown' in this._splitButton)) { - this._handleTouchAndPointerEvent(); + this._handleTouchAndPointerEvent(ev); } } private _onPointerDown = (ev: PointerEvent): void => { if (ev.pointerType === 'touch') { - this._handleTouchAndPointerEvent(); + this._handleTouchAndPointerEvent(ev); ev.preventDefault(); ev.stopImmediatePropagation(); } } - private _handleTouchAndPointerEvent() { + private _handleTouchAndPointerEvent(ev: React.TouchEvent | PointerEvent) { const { onTap } = this.props; if (onTap) { - onTap(); + onTap(ev); } // If we already have an existing timeout from a previous touch/pointer event // cancel that timeout so we can set a new one. diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.types.ts b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.types.ts index 0f06a4f163b92..c13e199231c7b 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.types.ts +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenuSplitButton.types.ts @@ -95,5 +95,5 @@ export interface IContextualMenuSplitButtonProps extends React.Props void; + onTap?: (ev: React.TouchEvent | PointerEvent) => void; } \ No newline at end of file From fb63fb88aee23c20afecaabc916817b9aea651a0 Mon Sep 17 00:00:00 2001 From: Josh Chang Date: Mon, 30 Apr 2018 14:57:25 -0700 Subject: [PATCH 37/39] fixed semicolon --- .../src/components/ContextualMenu/ContextualMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx index 6585356053986..89e566bdc9a0b 100644 --- a/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx +++ b/packages/office-ui-fabric-react/src/components/ContextualMenu/ContextualMenu.tsx @@ -976,6 +976,6 @@ export class ContextualMenu extends BaseComponent | PointerEvent) => { - this._cancelSubMenuTimer() + this._cancelSubMenuTimer(); } } From e7d02292ca2e19f5c258c79dc77665508f55da33 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Tue, 1 May 2018 13:30:11 -0700 Subject: [PATCH 38/39] updated bundle size --- scripts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package.json b/scripts/package.json index b4c367f9708a1..105b5321beb23 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -50,7 +50,7 @@ "bundlesize": [ { "path": "../apps/test-bundle-button/dist/test-bundle-button.min.js", - "maxSize": "48 kB" + "maxSize": "48.1 kB" } ] } \ No newline at end of file From d3b1d13f5d4235a32b22beda26bdf498e6a93fa8 Mon Sep 17 00:00:00 2001 From: "REDMOND\\chiechan" Date: Wed, 2 May 2018 14:19:13 -0700 Subject: [PATCH 39/39] fixed bundle size to include keytip change --- scripts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package.json b/scripts/package.json index 602e98bd80708..c6ce5067e8edc 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -50,7 +50,7 @@ "bundlesize": [ { "path": "../apps/test-bundle-button/dist/test-bundle-button.min.js", - "maxSize": "49.4 kB" + "maxSize": "49.6 kB" } ] } \ No newline at end of file