diff --git a/src/components/checkbox/checkbox.component.spec.ts b/src/components/checkbox/checkbox.component.spec.ts index 864ff0a..11af743 100644 --- a/src/components/checkbox/checkbox.component.spec.ts +++ b/src/components/checkbox/checkbox.component.spec.ts @@ -128,6 +128,26 @@ describe('Checkbox', () => { ) ); + it('should not emit a single "change" when the checkbox is readonly', + componentTest(() => TestComponent, ` + + `, + fixture => { + const nativeInput: HTMLInputElement = fixture.nativeElement.querySelector('input'); + const instance: TestComponent = fixture.componentInstance; + fixture.detectChanges(); + const spy = instance.onChange = jasmine.createSpy('onChange'); + + nativeInput.click(); + tick(); + fixture.detectChanges(); + + expect(instance.onChange).toHaveBeenCalledTimes(0); + expect(spy.calls.count()).toBe(0); + } + ) + ); + it('should emit "blur" with current check state when the native input blurs', componentTest(() => TestComponent, ` (); + _readonly: boolean = false; + checkState: CheckState = false; tabbedFocus: boolean = false; @@ -171,6 +183,10 @@ export class Checkbox implements ControlValueAccessor { this.fixInitialAnimation(); } + onClick(): boolean { + return !this.readonly; + } + onInputChanged(e: Event, input: HTMLInputElement): boolean { if (e) { e.stopPropagation(); @@ -197,6 +213,10 @@ export class Checkbox implements ControlValueAccessor { this.disabled = disabled; this.changeDetector.markForCheck(); } + setReadOnlyState(readonly: boolean): void { + this.readonly = readonly; + this.changeDetector.markForCheck(); + } private onChange: Function = () => {}; private onTouched: Function = () => {}; diff --git a/src/components/checkbox/checkbox.tpl.html b/src/components/checkbox/checkbox.tpl.html index cb76370..ff72356 100644 --- a/src/components/checkbox/checkbox.tpl.html +++ b/src/components/checkbox/checkbox.tpl.html @@ -11,7 +11,7 @@ (blur)="onBlur()" (focus)="onFocus()" (change)="onInputChanged($event, input)" - + (click)="onClick()" [class.tabbed]="tabbedFocus" #input diff --git a/src/components/date-time-picker/date-time-picker.component.spec.ts b/src/components/date-time-picker/date-time-picker.component.spec.ts index e782c1c..74fd4e8 100644 --- a/src/components/date-time-picker/date-time-picker.component.spec.ts +++ b/src/components/date-time-picker/date-time-picker.component.spec.ts @@ -30,6 +30,8 @@ import {OverlayHostService} from '../overlay-host/overlay-host.service'; import {DateTimePickerFormatProvider} from './date-time-picker-format-provider.service'; import {DateTimePickerModal} from './date-time-picker-modal.component'; import {DateTimePicker} from './date-time-picker.component'; +import { crossBrowserInitKeyboardEvent, KeyboardEventConfig } from '../../testing/keyboard-event'; +import { KeyCode } from '../../common/keycodes'; const TEST_TIMESTAMP: number = 1457971763; @@ -161,6 +163,12 @@ describe('DateTimePicker:', () => { describe('input display:', () => { + function dispatchKeyboardEvent(keyCode: number, element: HTMLElement, options?: KeyboardEventConfig): void { + let mergedOptions = Object.assign({ keyCode, bubbles: true }, options); + let event = crossBrowserInitKeyboardEvent('keypress', mergedOptions); + element.dispatchEvent(event); + } + function inputValue(fixture: ComponentFixture): string { fixture.detectChanges(); return fixture.nativeElement.querySelector('input').value.trim(); @@ -277,6 +285,35 @@ describe('DateTimePicker:', () => { ) ); + it('does not emit "clear" and "change" when the date picker is readonly', + componentTest(() => TestComponent, ` + + `, + (fixture, testComponent) => { + fixture.detectChanges(); + tick(); + + expect(testComponent.onClear).not.toHaveBeenCalled(); + + const clearButton = fixture.debugElement.query(By.css('gtx-button')); + clearButton.triggerEventHandler('click', document.createEvent('Event')); + fixture.detectChanges(); + + expect(testComponent.onClear).toHaveBeenCalledTimes(0); + + const input = fixture.debugElement.query(By.css('gtx-input')); + input.triggerEventHandler('click', document.createEvent('Event')); + fixture.detectChanges(); + + expect(testComponent.onChange).toHaveBeenCalledTimes(0); + + dispatchKeyboardEvent(KeyCode.Enter, input.nativeElement) + + expect(testComponent.onChange).toHaveBeenCalledTimes(0); + } + ) + ); + it('does not clear its value when clicking the clear button if the date picker is disabled', componentTest(() => TestComponent, ` (); @@ -109,6 +114,7 @@ export class DateTimePicker implements ControlValueAccessor, OnInit, OnDestroy { _clearable: boolean = false; _selectYear: boolean = false; _disabled: boolean = false; + _readonly: boolean = false; displayValue: string = ''; /** @internal */ private value: Moment; @@ -150,7 +156,7 @@ export class DateTimePicker implements ControlValueAccessor, OnInit, OnDestroy { } handleEnterKey(event: KeyboardEvent): void { - if (event.keyCode === 13 && !this._disabled) { + if (event.keyCode === 13 && !this._disabled && !this._readonly) { this.showModal(); } } @@ -209,6 +215,11 @@ export class DateTimePicker implements ControlValueAccessor, OnInit, OnDestroy { this.changeDetector.markForCheck(); } + setReadOnlyState(readonly: boolean): void { + this.readonly = readonly; + this.changeDetector.markForCheck(); + } + /** Format date to a human-readable string for displaying in the component's input field. */ updateDisplayValue(): void { if (!this.value) { diff --git a/src/components/date-time-picker/date-time-picker.tpl.html b/src/components/date-time-picker/date-time-picker.tpl.html index 0b1a523..41a8973 100644 --- a/src/components/date-time-picker/date-time-picker.tpl.html +++ b/src/components/date-time-picker/date-time-picker.tpl.html @@ -1,17 +1,17 @@ + (click)="!_disabled && !_readonly && clearDateTime()"> clear diff --git a/src/components/dropdown-list/dropdown-list.component.ts b/src/components/dropdown-list/dropdown-list.component.ts index 3701f25..6e8fe6d 100644 --- a/src/components/dropdown-list/dropdown-list.component.ts +++ b/src/components/dropdown-list/dropdown-list.component.ts @@ -85,6 +85,7 @@ export class DropdownList implements OnDestroy { @Output() close = new EventEmitter(); private _disabled: boolean = false; + private _readonly: boolean = false; private overlayHostView: ViewContainerRef; private scrollMaskFactory: ScrollMask; private scrollMaskRef: ComponentRef; @@ -164,6 +165,17 @@ export class DropdownList implements OnDestroy { this._disabled = coerceToBoolean(val); } + /** + * If true, the dropdown will be in readonly mode. + */ + @Input() + get readonly(): boolean { + return this._readonly; + } + set readonly(value: any) { + this._readonly = coerceToBoolean(value); + } + get isOpen(): boolean { return !!this.contentComponentRef; } @@ -217,7 +229,7 @@ export class DropdownList implements OnDestroy { * Open the dropdown contents in the correct position. */ openDropdown(): void { - if (this._disabled) { + if (this._disabled || this._readonly) { return; } this.contentComponentRef = this.overlayHostView.createComponent(DropdownContentWrapper, null); diff --git a/src/components/range/range.component.spec.ts b/src/components/range/range.component.spec.ts index 818b27a..895769e 100644 --- a/src/components/range/range.component.spec.ts +++ b/src/components/range/range.component.spec.ts @@ -68,6 +68,26 @@ describe('Range:', () => { ) ); + it('does not update the value when readonly attribute is true', + componentTest (() => TestComponent, ` + `, + (fixture, instance) => { + let nativeInput: HTMLInputElement = fixture.nativeElement.querySelector('input'); + fixture.detectChanges(); + instance.onChange = jasmine.createSpy('onChange'); + triggerInputEvent(nativeInput); + tick(); + expect(instance.onChange).toHaveBeenCalledTimes(0); + + instance.onInput = jasmine.createSpy('onInput'); + triggerInputEvent(nativeInput); + tick(); + expect(instance.onInput).toHaveBeenCalledTimes(0); + } + ) + ); + it('emits "blur" with the current value when its native input blurs', componentTest(() => TestComponent, ` e.target; return Number(target.value); diff --git a/src/components/range/range.tpl.html b/src/components/range/range.tpl.html index 69c7496..09eaff1 100644 --- a/src/components/range/range.tpl.html +++ b/src/components/range/range.tpl.html @@ -6,21 +6,21 @@ [attr.name]="name" [required]="required" [attr.step]="step" - [attr.id]="id" (blur)="onBlur($event)" (change)="onChangeEvent($event)" (focus)="onFocus($event)" (input)="onInput($event)" - (mousedown)="onMousedown($event)" - (mouseup)="onMouseup()" + (mousedown)="!readonly && onMousedown($event)" + (mouseup)="!readonly && onMouseup()" (mousemove)="onMousemove($event)" #input > {{ currentValue }} diff --git a/src/components/select/select.component.spec.ts b/src/components/select/select.component.spec.ts index ce1d0d2..09681bc 100644 --- a/src/components/select/select.component.spec.ts +++ b/src/components/select/select.component.spec.ts @@ -392,6 +392,23 @@ describe('Select:', () => { }) ); + it('does not open the dropdown when the it is readonly', + componentTest(() => TestComponent, ` + + {{ option }} + + `, + (fixture, instance) => { + fixture.detectChanges(); + tick(); + clickSelectAndOpen(fixture); + const listItems = getListItems(fixture); + expect(listItems).toHaveSize(0); + tick(1000); + } + ) + ); + it('should open when space is pressed', componentTest(() => TestComponent, fixture => { fixture.detectChanges(); diff --git a/src/components/select/select.component.ts b/src/components/select/select.component.ts index cb345f9..e4576b4 100644 --- a/src/components/select/select.component.ts +++ b/src/components/select/select.component.ts @@ -78,6 +78,17 @@ export class Select implements ControlValueAccessor { this._disabled = coerceToBoolean(value); } + /** + * Sets the readonly state. + */ + @Input() + get readonly(): boolean { + return this._readonly; + } + set readonly(value: any) { + this._readonly = coerceToBoolean(value); + } + /** * When set to true, allows multiple options to be selected. In this case, the input value should be * an array of strings; events will emit an array of strings. @@ -132,6 +143,7 @@ export class Select implements ControlValueAccessor { _clearable: boolean = false; _disabled: boolean = false; + _readonly: boolean = false; private preventDeselect: boolean = false; @ViewChild(DropdownList, { static: true }) private dropdownList: DropdownList; @ViewChild(DropdownContent, { static: true }) private dropdownContent: DropdownContent; @@ -299,6 +311,11 @@ export class Select implements ControlValueAccessor { this.changeDetector.markForCheck(); } + setReadOnlyState(isReadOnly: boolean): void { + this._readonly = isReadOnly; + this.changeDetector.markForCheck(); + } + /** Clears the selected value and emits `null` with the `change` event. */ clearSelection(): void { this.selectedOptions = []; diff --git a/src/components/select/select.tpl.html b/src/components/select/select.tpl.html index 7c7a0aa..be3cb07 100644 --- a/src/components/select/select.tpl.html +++ b/src/components/select/select.tpl.html @@ -2,6 +2,7 @@ belowTrigger="false" [sticky]="multiple" [disabled]="disabled" + [readonly]="readonly" (open)="dropdownOpened()" [class.clearable]="_clearable"> diff --git a/src/docs/pages/checkbox-demo/checkbox-demo.tpl.html b/src/docs/pages/checkbox-demo/checkbox-demo.tpl.html index 7aa06fa..ca5d3e6 100644 --- a/src/docs/pages/checkbox-demo/checkbox-demo.tpl.html +++ b/src/docs/pages/checkbox-demo/checkbox-demo.tpl.html @@ -83,3 +83,18 @@ Demos '> + + + + + + ReadOnly: {{ readonly | json }} + Example: {{ booleanVariable | json }} + + + + + diff --git a/src/docs/pages/checkbox-demo/checkbox-demo.ts b/src/docs/pages/checkbox-demo/checkbox-demo.ts index 37e0963..5db9019 100644 --- a/src/docs/pages/checkbox-demo/checkbox-demo.ts +++ b/src/docs/pages/checkbox-demo/checkbox-demo.ts @@ -7,7 +7,9 @@ export class CheckboxDemo { componentSource: string = require('!!raw-loader!../../../components/checkbox/checkbox.component.ts'); + readonly: boolean = false; someBoolean: boolean = false; + booleanVariable: boolean = true; checkStates: any = { A: true, B: false, diff --git a/src/docs/pages/date-time-picker-demo/date-time-picker-demo.tpl.html b/src/docs/pages/date-time-picker-demo/date-time-picker-demo.tpl.html index 6075d4e..a4348fe 100644 --- a/src/docs/pages/date-time-picker-demo/date-time-picker-demo.tpl.html +++ b/src/docs/pages/date-time-picker-demo/date-time-picker-demo.tpl.html @@ -111,3 +111,21 @@ Internationalization '> + + + + + + + + + + + + diff --git a/src/docs/pages/date-time-picker-demo/date-time-picker-demo.ts b/src/docs/pages/date-time-picker-demo/date-time-picker-demo.ts index c63fa75..b549f7f 100644 --- a/src/docs/pages/date-time-picker-demo/date-time-picker-demo.ts +++ b/src/docs/pages/date-time-picker-demo/date-time-picker-demo.ts @@ -10,4 +10,5 @@ export class DateTimePickerDemo { demoProviderSource = (require('!!raw-loader!./demo-format-provider.ts') as string).split('\n').slice(3).join('\n'); timestamp: number = 1457971763; + readonly: boolean = false; } diff --git a/src/docs/pages/range-demo/range-demo.tpl.html b/src/docs/pages/range-demo/range-demo.tpl.html index f79b1f0..d2439c5 100644 --- a/src/docs/pages/range-demo/range-demo.tpl.html +++ b/src/docs/pages/range-demo/range-demo.tpl.html @@ -69,3 +69,16 @@ Demos + + + + + + + + + + diff --git a/src/docs/pages/range-demo/range-demo.ts b/src/docs/pages/range-demo/range-demo.ts index 1d9cb70..31df839 100644 --- a/src/docs/pages/range-demo/range-demo.ts +++ b/src/docs/pages/range-demo/range-demo.ts @@ -7,5 +7,7 @@ export class RangeDemo { componentSource: string = require('!!raw-loader!../../../components/range/range.component.ts'); rangeValDynamic: number = 35; rangeVal: any = 0; + newRangeVal: any = 25; showThumb: boolean = true; + readonly: boolean = false; } diff --git a/src/docs/pages/select-demo/select-demo.tpl.html b/src/docs/pages/select-demo/select-demo.tpl.html index 61f51e5..31a959b 100644 --- a/src/docs/pages/select-demo/select-demo.tpl.html +++ b/src/docs/pages/select-demo/select-demo.tpl.html @@ -385,3 +385,26 @@ Demos '> + + + + + + + + + + + Value: {{ selectVal | json }} + + + + + + diff --git a/src/docs/pages/select-demo/select-demo.ts b/src/docs/pages/select-demo/select-demo.ts index 77a0bcb..87f20d8 100644 --- a/src/docs/pages/select-demo/select-demo.ts +++ b/src/docs/pages/select-demo/select-demo.ts @@ -24,4 +24,5 @@ export class SelectDemo { disableEntireControl: boolean = false; disableSingleOption: boolean = false; disableOptionGroup: boolean = false; + readonly: boolean = false; }
ReadOnly: {{ readonly | json }}
{{ readonly | json }}
Example: {{ booleanVariable | json }}
{{ booleanVariable | json }}
{{ selectVal | json }}