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(slide-toggle): support native tabindex attribute #6613

Merged
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
48 changes: 48 additions & 0 deletions src/lib/core/common-behaviors/tabindex.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {mixinTabIndex} from './tabindex';

describe('mixinTabIndex', () => {

it('should augment an existing class with a tabIndex property', () => {
const classWithMixin = mixinTabIndex(TestClass);
const instance = new classWithMixin();

expect(instance.tabIndex)
.toBe(0, 'Expected the mixed-into class to have a tabIndex property');

instance.tabIndex = 4;

expect(instance.tabIndex)
.toBe(4, 'Expected the mixed-into class to have an updated tabIndex property');
});

it('should set tabIndex to `-1` if the disabled property is set to true', () => {
const classWithMixin = mixinTabIndex(TestClass);
const instance = new classWithMixin();

expect(instance.tabIndex)
.toBe(0, 'Expected tabIndex to be set to 0 initially');

instance.disabled = true;

expect(instance.tabIndex)
.toBe(-1, 'Expected tabIndex to be set to -1 if the disabled property is set to true');
});

it('should allow having a custom default tabIndex value', () => {
const classWithMixin = mixinTabIndex(TestClass, 20);
const instance = new classWithMixin();

expect(instance.tabIndex)
.toBe(20, 'Expected tabIndex to be set to 20 initially');

instance.tabIndex = 0;

expect(instance.tabIndex)
.toBe(0, 'Expected tabIndex to still support 0 as value');
});

});

class TestClass {
disabled = false;
}
34 changes: 34 additions & 0 deletions src/lib/core/common-behaviors/tabindex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {Constructor} from './constructor';
import {CanDisable} from './disabled';

/** @docs-private */
export interface HasTabIndex {
tabIndex: number;
}

/** Mixin to augment a directive with a `tabIndex` property. */
export function mixinTabIndex<T extends Constructor<CanDisable>>(base: T, defaultTabIndex = 0)
: Constructor<HasTabIndex> & T {
return class extends base {
private _tabIndex: number = defaultTabIndex;

get tabIndex(): number { return this.disabled ? -1 : this._tabIndex; }
set tabIndex(value: number) {
// If the specified tabIndex value is null or undefined, fall back to the default value.
this._tabIndex = value != null ? value : defaultTabIndex;
}

constructor(...args: any[]) {
super(...args);
}
};
}

22 changes: 6 additions & 16 deletions src/lib/select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import {
PlaceholderOptions
} from '../core/placeholder/placeholder-options';
import {Platform} from '@angular/cdk/platform';
import {HasTabIndex, mixinTabIndex} from '../core/common-behaviors/tabindex';

/**
* The following style constants are necessary to save here in order
Expand Down Expand Up @@ -155,7 +156,8 @@ export class MdSelectChange {
export class MdSelectBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdSelectMixinBase = mixinColor(mixinDisabled(MdSelectBase), 'primary');
export const _MdSelectMixinBase =
mixinTabIndex(mixinColor(mixinDisabled(MdSelectBase), 'primary'));


/**
Expand All @@ -172,7 +174,7 @@ export class MdSelectTrigger {}
selector: 'md-select, mat-select',
templateUrl: 'select.html',
styleUrls: ['select.css'],
inputs: ['color', 'disabled'],
inputs: ['color', 'disabled', 'tabIndex'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
Expand Down Expand Up @@ -200,7 +202,7 @@ export class MdSelectTrigger {}
exportAs: 'mdSelect',
})
export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, OnDestroy, OnInit,
ControlValueAccessor, CanColor, CanDisable {
ControlValueAccessor, CanColor, CanDisable, HasTabIndex {
/** Whether or not the overlay panel is open. */
private _panelOpen = false;

Expand Down Expand Up @@ -234,9 +236,6 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
/** The animation state of the placeholder. */
private _placeholderState = '';

/** Tab index for the element. */
private _tabIndex: number;

/** Deals with configuring placeholder options */
private _placeholderOptions: PlaceholderOptions;

Expand Down Expand Up @@ -371,15 +370,6 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
}
private _floatPlaceholder: FloatPlaceholderType;

/** Tab index for the select element. */
@Input()
get tabIndex(): number { return this.disabled ? -1 : this._tabIndex; }
set tabIndex(value: number) {
if (typeof value !== 'undefined') {
this._tabIndex = value;
}
}

/** Value of the select control. */
@Input()
get value() { return this._value; }
Expand Down Expand Up @@ -446,7 +436,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
this._control.valueAccessor = this;
}

this._tabIndex = parseInt(tabIndex) || 0;
this.tabIndex = parseInt(tabIndex) || 0;
this._placeholderOptions = placeholderOptions ? placeholderOptions : {};
this.floatPlaceholder = this._placeholderOptions.float || 'auto';
}
Expand Down
19 changes: 18 additions & 1 deletion src/lib/slide-toggle/slide-toggle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('MdSlideToggle without forms', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdSlideToggleModule],
declarations: [SlideToggleBasic],
declarations: [SlideToggleBasic, SlideToggleWithTabindexAttr],
providers: [
{provide: HAMMER_GESTURE_CONFIG, useFactory: () => gestureConfig = new TestGestureConfig()}
]
Expand Down Expand Up @@ -333,6 +333,18 @@ describe('MdSlideToggle without forms', () => {

expect(fixture.componentInstance.lastEvent).toBeFalsy();
}));

it('should be able to set the tabindex via the native attribute', async(() => {
const fixture = TestBed.createComponent(SlideToggleWithTabindexAttr);

fixture.detectChanges();

const slideToggle = fixture.debugElement
.query(By.directive(MdSlideToggle)).componentInstance as MdSlideToggle;

expect(slideToggle.tabIndex)
.toBe(5, 'Expected tabIndex property to have been set based on the native attribute');
}));
});

describe('with dragging', () => {
Expand Down Expand Up @@ -789,3 +801,8 @@ class SlideToggleWithModel {
class SlideToggleWithFormControl {
formControl = new FormControl();
}

@Component({
template: `<md-slide-toggle tabindex="5"></md-slide-toggle>`
})
class SlideToggleWithTabindexAttr {}
16 changes: 9 additions & 7 deletions src/lib/slide-toggle/slide-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import {
AfterContentInit,
Attribute,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Expand Down Expand Up @@ -35,6 +36,7 @@ import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';
import {HasTabIndex, mixinTabIndex} from '../core/common-behaviors/tabindex';

// Increasing integer for generating unique ids for slide-toggle components.
let nextUniqueId = 0;
Expand All @@ -57,7 +59,7 @@ export class MdSlideToggleBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdSlideToggleMixinBase =
mixinColor(mixinDisableRipple(mixinDisabled(MdSlideToggleBase)), 'accent');
mixinTabIndex(mixinColor(mixinDisableRipple(mixinDisabled(MdSlideToggleBase)), 'accent'));

/** Represents a slidable "switch" toggle that can be moved between on and off. */
@Component({
Expand All @@ -73,12 +75,12 @@ export const _MdSlideToggleMixinBase =
templateUrl: 'slide-toggle.html',
styleUrls: ['slide-toggle.css'],
providers: [MD_SLIDE_TOGGLE_VALUE_ACCESSOR],
inputs: ['disabled', 'disableRipple', 'color'],
inputs: ['disabled', 'disableRipple', 'color', 'tabIndex'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MdSlideToggle extends _MdSlideToggleMixinBase implements OnDestroy, AfterContentInit,
ControlValueAccessor, CanDisable, CanColor, CanDisableRipple {
ControlValueAccessor, CanDisable, CanColor, HasTabIndex, CanDisableRipple {

private onChange = (_: any) => {};
private onTouched = () => {};
Expand All @@ -97,9 +99,6 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase implements OnDestroy,
/** A unique id for the slide-toggle input. If none is supplied, it will be auto-generated. */
@Input() id: string = this._uniqueId;

/** Used to specify the tabIndex value for the underlying input element. */
@Input() tabIndex: number = 0;

/** Whether the label should appear after or before the slide-toggle. Defaults to 'after' */
@Input() labelPosition: 'before' | 'after' = 'after';

Expand Down Expand Up @@ -139,8 +138,11 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase implements OnDestroy,
renderer: Renderer2,
private _platform: Platform,
private _focusOriginMonitor: FocusOriginMonitor,
private _changeDetectorRef: ChangeDetectorRef) {
private _changeDetectorRef: ChangeDetectorRef,
@Attribute('tabindex') tabIndex: string) {
super(renderer, elementRef);

this.tabIndex = parseInt(tabIndex) || 0;
}

ngAfterContentInit() {
Expand Down