Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(time picker): IE cursor flickering on spinning #3539

Merged
merged 6 commits into from
Jan 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ export class IgxMaskDirective implements OnInit, ControlValueAccessor {
public onKeydown(event): void {
const key = event.keyCode || event.charCode;

if (isIE() && this._stopPropagation) {
this._stopPropagation = false;
}

if (key === KEYS.Ctrl) {
this._ctrlDown = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
[focusedValuePipe]="inputFormat"
[promptChar]="promptChar"
[value]="displayValue"
(keydown)="onKeydown($event)"
(input)="onInput($event)"
(blur)="onBlur($event)"
(focus)="onFocus($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ describe('IgxTimePicker', () => {
expect(input.nativeElement.value).toBe('4:5 AM');
}));

it('should correct spin (arrow buttons) on empty value (dropdown mode)', (() => {
it('should correct spin (arrow buttons) on empty value (dropdown mode)', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxTimePickerDropDownNoValueComponent);
fixture.detectChanges();

Expand All @@ -941,12 +941,11 @@ describe('IgxTimePicker', () => {
fixture.detectChanges();

UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, true);
fixture.detectChanges();

input.nativeElement.setSelectionRange(1, 1);
tick(100);
fixture.detectChanges();

UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, true);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('01:00 AM', 'Hours spin failed');
Expand Down Expand Up @@ -1009,7 +1008,7 @@ describe('IgxTimePicker', () => {
expect(input.nativeElement.value).toEqual(customValue);
}));

it('should increase and decrease hours/minutes/AMPM, where the caret is, using arrows and mousewheel', (() => {
it('should increase and decrease hours/minutes/AMPM, where the caret is, using arrows and mousewheel', fakeAsync(() => {
fixture.detectChanges();

// initial input value is 05:45 PM
Expand All @@ -1022,16 +1021,14 @@ describe('IgxTimePicker', () => {

// press arrow down
UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', input.nativeElement, true);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('04:45 PM', 'ArrowDown on hours failed');

// position caret at the hours
input.nativeElement.setSelectionRange(1, 1);
fixture.detectChanges();

// mousewheel up
UIInteractions.simulateWheelEvent(input.nativeElement, 0, -10);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('05:45 PM', 'MouseWheel Up on hours dailed');
Expand All @@ -1040,14 +1037,14 @@ describe('IgxTimePicker', () => {
// position caret at the minutes and mousewheel down
input.nativeElement.setSelectionRange(3, 3);
UIInteractions.simulateWheelEvent(input.nativeElement, 0, 10);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('05:44 PM', 'MouseWheel Down on minutes failed');

input.nativeElement.setSelectionRange(3, 3);
fixture.detectChanges();
// press arrow up
UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, true);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('05:45 PM', 'ArrowUp on minutes failed');
Expand All @@ -1056,23 +1053,25 @@ describe('IgxTimePicker', () => {
// position caret at AMPM and arrow down
input.nativeElement.setSelectionRange(7, 7);
UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', input.nativeElement, true);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('05:45 AM', 'ArrowDown on AMPM failed');

input.nativeElement.setSelectionRange(7, 7);
fixture.detectChanges();
// mousewheel up
UIInteractions.simulateWheelEvent(input.nativeElement, 0, -10);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('05:45 PM', 'MouseWheel Up on AMPM failed');

// test full hours
input.nativeElement.setSelectionRange(0, 0);
tick(100);
fixture.detectChanges();

UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, true);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('06:45 PM', 'MouseWheel Up on AMPM failed');
Expand Down Expand Up @@ -1185,7 +1184,7 @@ describe('IgxTimePicker', () => {
expect(input.nativeElement.value).toBe(customValue, 'SpinLoop did not stop on AMPM');
}));

it('should spinloop on correct time after max or min values', (() => {
it('should spinloop on correct time after max or min values', fakeAsync(() => {
fixture.detectChanges();

const customValue = '08:05 AM';
Expand All @@ -1207,6 +1206,7 @@ describe('IgxTimePicker', () => {
fixture.detectChanges();

UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', input.nativeElement, true);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('11:05 AM', 'SpinLoop Down wrong time');
Expand All @@ -1224,6 +1224,7 @@ describe('IgxTimePicker', () => {
fixture.detectChanges();

UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, true);
tick(100);
fixture.detectChanges();

expect(input.nativeElement.value).toBe('09:03 AM', 'SpinLoop Up wrong time');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
ViewChild,
Inject,
ContentChild,
Injectable
Injectable,
AfterViewInit
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { HAMMER_GESTURE_CONFIG, HammerGestureConfig } from '@angular/platform-browser';
Expand All @@ -30,14 +31,14 @@ import {
IgxMinuteItemDirective,
IgxTimePickerTemplateDirective
} from './time-picker.directives';
import { Subject } from 'rxjs';
import { Subject, fromEvent, interval, animationFrameScheduler } from 'rxjs';
import { EditorProvider } from '../core/edit-provider';
import { IgxTimePickerBase, IGX_TIME_PICKER_COMPONENT, TimePickerInteractionMode } from './time-picker.common';
import { IgxOverlayService } from '../services/overlay/overlay';
import { NoOpScrollStrategy } from '../services/overlay/scroll';
import { ConnectedPositioningStrategy } from '../services/overlay/position';
import { HorizontalAlignment, VerticalAlignment, PositionSettings, OverlaySettings } from '../services/overlay/utilities';
import { takeUntil, filter } from 'rxjs/operators';
import { takeUntil, filter, throttle } from 'rxjs/operators';
import { IgxButtonModule } from '../directives/button/button.directive';
import { IgxMaskModule } from '../directives/mask/mask.directive';
import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive';
Expand Down Expand Up @@ -94,7 +95,8 @@ export class IgxTimePickerComponent implements
ControlValueAccessor,
EditorProvider,
OnInit,
OnDestroy {
OnDestroy,
AfterViewInit {

/**
* An @Input property that sets the value of the `id` attribute.
Expand Down Expand Up @@ -708,6 +710,18 @@ export class IgxTimePickerComponent implements
this._dialogOverlaySettings = {};
}

/**
* @hidden
*/
public ngAfterViewInit(): void {
if (this.mode === TimePickerInteractionMode.dropdown) {
fromEvent(this.input.nativeElement, 'keydown').pipe(
throttle(() => interval(0, animationFrameScheduler)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no sense to throttle events for 0 ms. I think setting the interval to 300 ms would make more sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interval(0, animationFrameScheduler) creates an observable that emits one event every time the browser is ready to display a new frame and then the throttle operator will emit a value from that source observable.

takeUntil(this._destroy$)
).subscribe((res) => this.onKeydown(res));
}
}

/**
* @hidden
*/
Expand Down Expand Up @@ -1600,7 +1614,10 @@ export class IgxTimePickerComponent implements
displayVal = this._formatTime(this.value, this.format);
}

this.displayValue = this.inputFormat.transform(displayVal);
// minor hack for preventing cursor jumping in IE
this._displayValue = this.inputFormat.transform(displayVal);
this.input.nativeElement.value = this._displayValue;
this._setCursorPosition(cursor);

requestAnimationFrame(() => {
this._setCursorPosition(cursor);
Expand Down