Skip to content

Commit bd1e80e

Browse files
authored
Merge branch 'master' into fix/toggle
2 parents fa49595 + 466166b commit bd1e80e

File tree

7 files changed

+199
-3
lines changed

7 files changed

+199
-3
lines changed

src/content-switcher/content-switcher-option.directive.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export class ContentSwitcherOption {
1515
this._active = value;
1616
this.selectedClass = value;
1717
this.ariaSelected = value;
18+
this.tabindex = value ? "0" : "-1";
1819
}
1920

2021
get active() {
@@ -29,6 +30,7 @@ export class ContentSwitcherOption {
2930
@HostBinding("class.bx--content-switcher--selected") selectedClass = false;
3031
@HostBinding("attr.role") role = "tab";
3132
@HostBinding("attr.aria-selected") ariaSelected = false;
33+
@HostBinding("attr.tabIndex") tabindex = "-1";
3234

3335
protected _active = false;
3436

@@ -37,4 +39,16 @@ export class ContentSwitcherOption {
3739
this.active = true;
3840
this.selected.emit(true);
3941
}
42+
43+
@HostListener("focus")
44+
onFocus() {
45+
this.active = true;
46+
}
47+
48+
@HostListener("blur", ["$event"])
49+
onBlur(event) {
50+
if (event.relatedTarget) {
51+
this.active = false;
52+
}
53+
}
4054
}

src/content-switcher/content-switcher.component.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import {
55
QueryList,
66
Output,
77
EventEmitter,
8-
AfterViewInit
8+
AfterViewInit,
9+
HostListener,
10+
ElementRef
911
} from "@angular/core";
1012
import { ContentSwitcherOption } from "./content-switcher-option.directive";
13+
import { isFocusInLastItem, isFocusInFirstItem } from "./../common/tab.service";
1114

1215
/**
1316
*
@@ -31,7 +34,10 @@ export class ContentSwitcher implements AfterViewInit {
3134

3235
@ContentChildren(ContentSwitcherOption) options: QueryList<ContentSwitcherOption>;
3336

37+
constructor(protected elementRef: ElementRef) {}
38+
3439
ngAfterViewInit() {
40+
this.options.first.active = true;
3541
this.options.forEach(option => {
3642
option.selected.subscribe(_ => {
3743
const active = option;
@@ -44,4 +50,43 @@ export class ContentSwitcher implements AfterViewInit {
4450
});
4551
});
4652
}
53+
54+
@HostListener("keydown", ["$event"])
55+
hostkeys(event: KeyboardEvent) {
56+
const buttonList = Array.from<any>(this.elementRef.nativeElement.querySelectorAll(".bx--content-switcher-btn"));
57+
58+
switch (event.key) {
59+
case "Right": // IE specific value
60+
case "ArrowRight":
61+
event.preventDefault();
62+
if (!isFocusInLastItem(event, buttonList)) {
63+
const index = buttonList.findIndex(item => item === event.target);
64+
buttonList[index + 1].focus();
65+
} else {
66+
buttonList[0].focus();
67+
}
68+
break;
69+
70+
case "Left": // IE specific value
71+
case "ArrowLeft":
72+
event.preventDefault();
73+
if (!isFocusInFirstItem(event, buttonList)) {
74+
const index = buttonList.findIndex(item => item === event.target);
75+
buttonList[index - 1].focus();
76+
} else {
77+
buttonList[buttonList.length - 1].focus();
78+
}
79+
break;
80+
81+
case "Home":
82+
event.preventDefault();
83+
buttonList[0].focus();
84+
break;
85+
86+
case "End":
87+
event.preventDefault();
88+
buttonList[buttonList.length - 1].focus();
89+
break;
90+
}
91+
}
4792
}

src/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export * from "./forms/forms.module";
1212
export * from "./i18n/i18n.module";
1313
export * from "./icon/icon.module";
1414
export * from "./input/input.module";
15+
export * from "./link/link.module";
1516
export * from "./list-group/list-group.module";
1617
export * from "./loading/loading.module";
1718
export * from "./modal/modal.module";
@@ -26,5 +27,3 @@ export * from "./switch/switch.module";
2627
export * from "./table/table.module";
2728
export * from "./tabs/tabs.module";
2829
export * from "./tiles/tiles.module";
29-
export * from "./toggle/toggle.module";
30-
export * from "./utils/position";

src/link/link.directive.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {
2+
Directive,
3+
HostBinding,
4+
Input
5+
} from "@angular/core";
6+
7+
/**
8+
* A convinence directive for applying styling to a link.
9+
*
10+
* Example:
11+
*
12+
* ```hmtl
13+
* <a href="#" ibmLink>A link</a>
14+
* ```
15+
*
16+
* See the [vanilla carbon docs](http://www.carbondesignsystem.com/components/link/code) for more detail.
17+
*/
18+
@Directive({
19+
selector: "[ibmLink]"
20+
})
21+
22+
export class Link {
23+
@HostBinding("class.bx--link") baseClass = true;
24+
25+
/**
26+
* Automatically set to `-1` when link is disabled.
27+
* @memberof Link
28+
*/
29+
@HostBinding("attr.tabindex") tabindex;
30+
31+
/**
32+
* Set to true to disable link.
33+
* @memberof Link
34+
*/
35+
@Input()
36+
@HostBinding("attr.aria-disabled")
37+
set disabled(disabled: boolean) {
38+
this._disabled = disabled;
39+
this.tabindex = this.disabled ? -1 : null;
40+
}
41+
42+
get disabled(): boolean {
43+
return this._disabled;
44+
}
45+
46+
private _disabled;
47+
}

src/link/link.module.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { NgModule } from "@angular/core";
2+
import { CommonModule } from "@angular/common";
3+
4+
import { Link } from "./link.directive";
5+
6+
export { Link } from "./link.directive";
7+
8+
@NgModule({
9+
declarations: [
10+
Link
11+
],
12+
exports: [
13+
Link
14+
],
15+
imports: [
16+
CommonModule
17+
]
18+
})
19+
export class LinkModule {}

src/link/link.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { TestBed, ComponentFixture } from "@angular/core/testing";
2+
import { Component, Input, DebugElement } from "@angular/core";
3+
import { By } from "@angular/platform-browser";
4+
5+
import { Link } from "./link.directive";
6+
7+
describe("Link", () => {
8+
it("should create a Link", () => {
9+
TestBed.configureTestingModule({
10+
declarations: [TestLinkComponent, Link]
11+
});
12+
13+
let fixture: ComponentFixture<TestLinkComponent> = TestBed.createComponent(TestLinkComponent);
14+
let component: TestLinkComponent = fixture.componentInstance;
15+
fixture.detectChanges();
16+
17+
expect(component).toBeTruthy();
18+
const directiveEl = fixture.debugElement.query(By.directive(Link));
19+
expect(directiveEl).not.toBeNull();
20+
expect(directiveEl.attributes["aria-disabled"]).toBe(null);
21+
expect(directiveEl.attributes["tabindex"]).toBe(null);
22+
expect(directiveEl.attributes["href"]).toBe("https://angular.carbondesignsystem.com/");
23+
});
24+
25+
it("should create a disabled link", () => {
26+
TestBed.configureTestingModule({
27+
declarations: [TestDisabledLinkComponent, Link]
28+
});
29+
30+
let fixture: ComponentFixture<TestDisabledLinkComponent> = TestBed.createComponent(TestDisabledLinkComponent);
31+
let component: TestDisabledLinkComponent = fixture.componentInstance;
32+
fixture.detectChanges();
33+
34+
const directiveEl = fixture.debugElement.query(By.directive(Link));
35+
expect(directiveEl.attributes["aria-disabled"]).toBe("true");
36+
expect(directiveEl.attributes["tabindex"]).toBe("-1");
37+
});
38+
});
39+
40+
@Component({
41+
template: `<a href="https://angular.carbondesignsystem.com/" ibmLink>link</a>`
42+
})
43+
class TestLinkComponent {
44+
}
45+
46+
@Component({
47+
template: `<a href="https://angular.carbondesignsystem.com/" [disabled]="1+1===2" ibmLink>link</a>`
48+
})
49+
class TestDisabledLinkComponent {
50+
}

src/link/link.stories.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { storiesOf, moduleMetadata } from "@storybook/angular";
2+
import { withKnobs, boolean } from "@storybook/addon-knobs/angular";
3+
4+
import { LinkModule } from "../";
5+
6+
storiesOf("Link", module)
7+
.addDecorator(
8+
moduleMetadata({
9+
imports: [
10+
LinkModule
11+
]
12+
})
13+
)
14+
.addDecorator(withKnobs)
15+
.add("Basic", () => ({
16+
template: `
17+
<a href="#" ibmLink [disabled]="disabled">link</a>
18+
`,
19+
props: {
20+
disabled: boolean("disabled", false)
21+
}
22+
}));

0 commit comments

Comments
 (0)