mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
committed by
Jeremy Elbourn
parent
22d70aeb8f
commit
9d51deb194
@@ -78,7 +78,7 @@ function getFocusStates(elements: ElementArrayFinder) {
|
||||
* @returns {webdriver.promise.Promise<Promise<boolean>[]>|webdriver.promise.Promise<T[]>}
|
||||
*/
|
||||
function getActiveStates(elements: ElementArrayFinder) {
|
||||
return getClassStates(elements, 'md-active');
|
||||
return getClassStates(elements, 'md-tab-active');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
[tabIndex]="selectedIndex == i ? 0 : -1"
|
||||
[attr.aria-controls]="_getTabContentId(i)"
|
||||
[attr.aria-selected]="selectedIndex == i"
|
||||
[class.md-active]="selectedIndex == i"
|
||||
[class.md-tab-active]="selectedIndex == i"
|
||||
[class.md-tab-disabled]="tab.disabled"
|
||||
(click)="focusIndex = selectedIndex = i">
|
||||
<template [portalHost]="tab.label"></template>
|
||||
</div>
|
||||
@@ -17,7 +18,7 @@
|
||||
role="tabpanel"
|
||||
*ngFor="let tab of _tabs; let i = index"
|
||||
[id]="_getTabContentId(i)"
|
||||
[class.md-active]="selectedIndex == i"
|
||||
[class.md-tab-active]="selectedIndex == i"
|
||||
[attr.aria-labelledby]="_getTabLabelId(i)">
|
||||
<template [ngIf]="selectedIndex == i">
|
||||
<template [portalHost]="tab.content"></template>
|
||||
|
||||
@@ -40,6 +40,11 @@ $md-tab-bar-height: 48px !default;
|
||||
}
|
||||
}
|
||||
|
||||
.md-tab-disabled {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/** The bottom section of the view; contains the tab bodies */
|
||||
.md-tab-body-wrapper {
|
||||
position: relative;
|
||||
|
||||
@@ -151,6 +151,92 @@ describe('MdTabGroup', () => {
|
||||
}));
|
||||
});
|
||||
|
||||
describe('disabled tabs', () => {
|
||||
let fixture: ComponentFixture<DisabledTabsTestApp>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
builder.createAsync(DisabledTabsTestApp).then(f => {
|
||||
fixture = f;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should disable the second tab', () => {
|
||||
let labels = fixture.debugElement.queryAll(By.css('.md-tab-label'));
|
||||
|
||||
expect(labels[1].nativeElement.classList.contains('md-tab-disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should skip over disabled tabs when navigating by keyboard', () => {
|
||||
let component: MdTabGroup = fixture.debugElement.query(By.css('md-tab-group'))
|
||||
.componentInstance;
|
||||
|
||||
component.focusIndex = 0;
|
||||
component.focusNextTab();
|
||||
|
||||
expect(component.focusIndex).toBe(2);
|
||||
|
||||
component.focusNextTab();
|
||||
expect(component.focusIndex).toBe(2);
|
||||
|
||||
component.focusPreviousTab();
|
||||
expect(component.focusIndex).toBe(0);
|
||||
|
||||
component.focusPreviousTab();
|
||||
expect(component.focusIndex).toBe(0);
|
||||
});
|
||||
|
||||
it('should ignore attempts to select a disabled tab', () => {
|
||||
let component: MdTabGroup = fixture.debugElement.query(By.css('md-tab-group'))
|
||||
.componentInstance;
|
||||
|
||||
component.selectedIndex = 0;
|
||||
expect(component.selectedIndex).toBe(0);
|
||||
|
||||
component.selectedIndex = 1;
|
||||
expect(component.selectedIndex).toBe(0);
|
||||
});
|
||||
|
||||
it('should ignore attempts to focus a disabled tab', () => {
|
||||
let component: MdTabGroup = fixture.debugElement.query(By.css('md-tab-group'))
|
||||
.componentInstance;
|
||||
|
||||
component.focusIndex = 0;
|
||||
expect(component.focusIndex).toBe(0);
|
||||
|
||||
component.focusIndex = 1;
|
||||
expect(component.focusIndex).toBe(0);
|
||||
});
|
||||
|
||||
it('should ignore attempts to set invalid selectedIndex', () => {
|
||||
let component: MdTabGroup = fixture.debugElement.query(By.css('md-tab-group'))
|
||||
.componentInstance;
|
||||
|
||||
component.selectedIndex = 0;
|
||||
expect(component.selectedIndex).toBe(0);
|
||||
|
||||
component.selectedIndex = -1;
|
||||
expect(component.selectedIndex).toBe(0);
|
||||
|
||||
component.selectedIndex = 4;
|
||||
expect(component.selectedIndex).toBe(0);
|
||||
});
|
||||
|
||||
it('should ignore attempts to set invalid focusIndex', () => {
|
||||
let component: MdTabGroup = fixture.debugElement.query(By.css('md-tab-group'))
|
||||
.componentInstance;
|
||||
|
||||
component.focusIndex = 0;
|
||||
expect(component.focusIndex).toBe(0);
|
||||
|
||||
component.focusIndex = -1;
|
||||
expect(component.focusIndex).toBe(0);
|
||||
|
||||
component.focusIndex = 4;
|
||||
expect(component.focusIndex).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('async tabs', () => {
|
||||
let fixture: ComponentFixture<AsyncTabsTestApp>;
|
||||
|
||||
@@ -173,7 +259,7 @@ describe('MdTabGroup', () => {
|
||||
|
||||
/**
|
||||
* Checks that the `selectedIndex` has been updated; checks that the label and body have the
|
||||
* `md-active` class
|
||||
* `md-tab-active` class
|
||||
*/
|
||||
function checkSelectedIndex(index: number, fixture: ComponentFixture<any>) {
|
||||
fixture.detectChanges();
|
||||
@@ -184,11 +270,11 @@ describe('MdTabGroup', () => {
|
||||
|
||||
let tabLabelElement = fixture.debugElement
|
||||
.query(By.css(`.md-tab-label:nth-of-type(${index + 1})`)).nativeElement;
|
||||
expect(tabLabelElement.classList.contains('md-active')).toBe(true);
|
||||
expect(tabLabelElement.classList.contains('md-tab-active')).toBe(true);
|
||||
|
||||
let tabContentElement = fixture.debugElement
|
||||
.query(By.css(`#${tabLabelElement.id}`)).nativeElement;
|
||||
expect(tabContentElement.classList.contains('md-active')).toBe(true);
|
||||
expect(tabContentElement.classList.contains('md-tab-active')).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,6 +312,27 @@ class SimpleTabsTestApp {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template: `
|
||||
<md-tab-group class="tab-group">
|
||||
<md-tab>
|
||||
<template md-tab-label>Tab One</template>
|
||||
<template md-tab-content>Tab one content</template>
|
||||
</md-tab>
|
||||
<md-tab disabled>
|
||||
<template md-tab-label>Tab Two</template>
|
||||
<template md-tab-content>Tab two content</template>
|
||||
</md-tab>
|
||||
<md-tab>
|
||||
<template md-tab-label>Tab Three</template>
|
||||
<template md-tab-content>Tab three content</template>
|
||||
</md-tab>
|
||||
</md-tab-group>
|
||||
`,
|
||||
})
|
||||
class DisabledTabsTestApp {}
|
||||
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template: `
|
||||
|
||||
+51
-15
@@ -43,6 +43,16 @@ export class MdTabChangeEvent {
|
||||
export class MdTab {
|
||||
@ContentChild(MdTabLabel) label: MdTabLabel;
|
||||
@ContentChild(MdTabContent) content: MdTabContent;
|
||||
|
||||
// TODO: Replace this when BooleanFieldValue is removed.
|
||||
private _disabled = false;
|
||||
@Input('disabled')
|
||||
set disabled(value: boolean) {
|
||||
this._disabled = (value != null && `${value}` !== 'false');
|
||||
}
|
||||
get disabled(): boolean {
|
||||
return this._disabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +77,7 @@ export class MdTabGroup {
|
||||
private _selectedIndex: number = 0;
|
||||
@Input()
|
||||
set selectedIndex(value: number) {
|
||||
if (value != this._selectedIndex) {
|
||||
if (value != this._selectedIndex && this.isValidIndex(value)) {
|
||||
this._selectedIndex = value;
|
||||
|
||||
if (this._isInitialized) {
|
||||
@@ -79,6 +89,19 @@ export class MdTabGroup {
|
||||
return this._selectedIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an index is valid. If the tabs are not ready yet, we assume that the user is
|
||||
* providing a valid index and return true.
|
||||
*/
|
||||
isValidIndex(index: number): boolean {
|
||||
if (this._tabs) {
|
||||
const tab = this._tabs.toArray()[index];
|
||||
return tab && !tab.disabled;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Output to enable support for two-way binding on `selectedIndex`. */
|
||||
@Output('selectedIndexChange') private get _selectedIndexChange(): Observable<number> {
|
||||
return this.selectChange.map(event => event.index);
|
||||
@@ -137,14 +160,16 @@ export class MdTabGroup {
|
||||
|
||||
/** When the focus index is set, we must manually send focus to the correct label */
|
||||
set focusIndex(value: number) {
|
||||
this._focusIndex = value;
|
||||
if (this.isValidIndex(value)) {
|
||||
this._focusIndex = value;
|
||||
|
||||
if (this._isInitialized) {
|
||||
this._onFocusChange.emit(this._createChangeEvent(value));
|
||||
}
|
||||
if (this._isInitialized) {
|
||||
this._onFocusChange.emit(this._createChangeEvent(value));
|
||||
}
|
||||
|
||||
if (this._labelWrappers && this._labelWrappers.length) {
|
||||
this._labelWrappers.toArray()[value].focus();
|
||||
if (this._labelWrappers && this._labelWrappers.length) {
|
||||
this._labelWrappers.toArray()[value].focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,18 +206,29 @@ export class MdTabGroup {
|
||||
}
|
||||
}
|
||||
|
||||
/** Increment the focus index by 1; prevent going over the number of tabs */
|
||||
focusNextTab(): void {
|
||||
if (this._labelWrappers && this.focusIndex < this._labelWrappers.length - 1) {
|
||||
this.focusIndex++;
|
||||
/**
|
||||
* Moves the focus left or right depending on the offset provided. Valid offsets are 1 and -1.
|
||||
*/
|
||||
moveFocus(offset: number) {
|
||||
if (this._labelWrappers) {
|
||||
const tabs: MdTab[] = this._tabs.toArray();
|
||||
for (let i = this.focusIndex + offset; i < tabs.length && i >= 0; i += offset) {
|
||||
if (this.isValidIndex(i)) {
|
||||
this.focusIndex = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Decrement the focus index by 1; prevent going below 0 */
|
||||
/** Increment the focus index by 1 until a valid tab is found. */
|
||||
focusNextTab(): void {
|
||||
this.moveFocus(1);
|
||||
}
|
||||
|
||||
/** Decrement the focus index by 1 until a valid tab is found. */
|
||||
focusPreviousTab(): void {
|
||||
if (this.focusIndex > 0) {
|
||||
this.focusIndex--;
|
||||
}
|
||||
this.moveFocus(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<h1>Tab Group Demo</h1>
|
||||
|
||||
<md-tab-group class="demo-tab-group">
|
||||
<md-tab *ngFor="let tab of tabs">
|
||||
<md-tab *ngFor="let tab of tabs; let i = index" [disabled]="i == 1">
|
||||
<template md-tab-label>{{tab.label}}</template>
|
||||
<template md-tab-content>
|
||||
{{tab.content}}
|
||||
@@ -16,7 +16,7 @@
|
||||
<h1>Async Tabs</h1>
|
||||
|
||||
<md-tab-group class="demo-tab-group">
|
||||
<md-tab *ngFor="let tab of asyncTabs | async">
|
||||
<md-tab *ngFor="let tab of asyncTabs | async; let i = index" [disabled]="i == 1">
|
||||
<template md-tab-label>{{tab.label}}</template>
|
||||
<template md-tab-content>
|
||||
{{tab.content}}
|
||||
|
||||
Reference in New Issue
Block a user