Skip to content

Commit 6fc8683

Browse files
authored
Merge pull request #473 from IBM/master
update breaking-changes
2 parents dbd09b0 + ec9cf11 commit 6fc8683

File tree

8 files changed

+193
-44
lines changed

8 files changed

+193
-44
lines changed

src/datepicker/datepicker.component.ts

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
import { Component, Input, Output, EventEmitter } from "@angular/core";
1+
import {
2+
Component,
3+
Input,
4+
Output,
5+
EventEmitter,
6+
ElementRef,
7+
OnDestroy
8+
} from "@angular/core";
29
import { FlatpickrOptions } from "ng2-flatpickr";
310
import rangePlugin from "flatpickr/dist/plugins/rangePlugin";
411

12+
513
@Component({
614
selector: "ibm-date-picker",
715
template: `
@@ -45,22 +53,18 @@ import rangePlugin from "flatpickr/dist/plugins/rangePlugin";
4553
</div>
4654
`
4755
})
48-
export class DatePicker {
56+
export class DatePicker implements OnDestroy {
4957
private static datePickerCount = 0;
5058

5159
/**
5260
* Select calendar range mode
53-
*
54-
* @memberof Datepicker
5561
*/
5662
@Input() range: boolean;
5763

5864
/**
5965
* Format of date
6066
*
6167
* For reference: https://flatpickr.js.org/formatting/
62-
*
63-
* @memberof Datepicker
6468
*/
6569
@Input() dateFormat = "m/d/Y";
6670

@@ -95,36 +99,59 @@ export class DatePicker {
9599
value: this.value
96100
};
97101

102+
constructor(protected elementRef: ElementRef) { }
103+
98104
doSelect(selectedValue) {
99105
this.valueChange.emit(selectedValue);
100106
}
101107

102108
updateClassNames() {
103-
const calendarContainer = document.querySelector(".flatpickr-calendar");
104-
const monthContainer = document.querySelector(".flatpickr-month");
105-
const weekdaysContainer = document.querySelector(".flatpickr-weekdays");
109+
const ng2FlatPickrElement = this.elementRef.nativeElement.querySelector(".ng2-flatpickr-input-container");
110+
ng2FlatPickrElement._flatpickr._positionCalendar();
111+
112+
// get all the possible flatpickrs in the document - we need to add classes to (potentially) all of them
113+
const calendarContainer = document.querySelectorAll(".flatpickr-calendar");
114+
const monthContainer = document.querySelectorAll(".flatpickr-month");
115+
const weekdaysContainer = document.querySelectorAll(".flatpickr-weekdays");
106116
const weekdayContainer = document.querySelectorAll(".flatpickr-weekday");
107-
const daysContainer = document.querySelector(".flatpickr-days");
117+
const daysContainer = document.querySelectorAll(".flatpickr-days");
108118
const dayContainer = document.querySelectorAll(".flatpickr-day");
109119

110-
calendarContainer.classList.add("bx--date-picker__calendar");
111-
monthContainer.classList.add("bx--date-picker__month");
112-
weekdaysContainer.classList.add("bx--date-picker__weekdays");
113-
daysContainer.classList.add("bx--date-picker__days");
114-
115-
Array.from(weekdayContainer).forEach(item => {
116-
const currentItem = item;
117-
currentItem.innerHTML = currentItem.innerHTML.replace(/\s+/g, "");
118-
currentItem.classList.add("bx--date-picker__weekday");
120+
// add classes to lists of elements
121+
const addClassIfNotExists = (classname: string, elementList: NodeListOf<Element>) => {
122+
Array.from(elementList).forEach(element => {
123+
if (!element.classList.contains(classname)) {
124+
element.classList.add(classname);
125+
}
126+
});
127+
};
128+
129+
// add classes (but only if they don't exist, small perf win)
130+
addClassIfNotExists("bx--date-picker__calendar", calendarContainer);
131+
addClassIfNotExists("bx--date-picker__month", monthContainer);
132+
addClassIfNotExists("bx--date-picker__weekdays", weekdaysContainer);
133+
addClassIfNotExists("bx--date-picker__days", daysContainer);
134+
135+
// add weekday classes and format the text
136+
Array.from(weekdayContainer).forEach(element => {
137+
element.innerHTML = element.innerHTML.replace(/\s+/g, "");
138+
element.classList.add("bx--date-picker__weekday");
119139
});
120140

121-
Array.from(dayContainer).forEach(item => {
122-
item.classList.add("bx--date-picker__day");
123-
if (item.classList.contains("today") && this.value.length > 0) {
124-
item.classList.add("no-border");
125-
} else if (item.classList.contains("today") && this.value.length === 0) {
126-
item.classList.remove("no-border");
141+
// add day classes and special case the "today" element based on `this.value`
142+
Array.from(dayContainer).forEach(element => {
143+
element.classList.add("bx--date-picker__day");
144+
if (element.classList.contains("today") && this.value.length > 0) {
145+
element.classList.add("no-border");
146+
} else if (element.classList.contains("today") && this.value.length === 0) {
147+
element.classList.remove("no-border");
127148
}
128149
});
129150
}
151+
152+
ngOnDestroy() {
153+
// clean up our flatpickr element - needed because the wrapper doesn't handle this
154+
const ng2FlatPickrElement = this.elementRef.nativeElement.querySelector(".ng2-flatpickr-input-container");
155+
ng2FlatPickrElement._flatpickr.destroy();
156+
}
130157
}

src/modal/modal.stories.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ModalModule } from "../";
55
import { Component, Input, Inject } from "@angular/core";
66
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
77
import { Modal, ModalService } from "../";
8-
import { ModalButton, AlertModalType } from "./alert-modal.interface";
8+
import { ModalButton, AlertModalType, ModalButtonType } from "./alert-modal.interface";
99
import { PlaceholderModule } from "./../placeholder/placeholder.module";
1010
import { BaseModal } from "./base-modal.class";
1111
import { ExperimentalComponenent } from "../../.storybook/experimental.component";
@@ -21,15 +21,34 @@ import { ExperimentalModule } from "../experimental.module";
2121
<p class="bx--modal-content__text">{{modalText}}</p>
2222
</section>
2323
<ibm-modal-footer>
24+
<button class="bx--btn bx--btn--secondary" (click)="showSecondaryModal()">Show Secondary Modal</button>
2425
<button class="bx--btn bx--btn--primary" modal-primary-focus (click)="closeModal()">Close</button>
2526
</ibm-modal-footer>
2627
</ibm-modal>
2728
`
2829
})
2930
class SampleModal extends BaseModal {
30-
constructor(@Inject("modalText") public modalText) {
31+
constructor(
32+
@Inject("modalText") public modalText,
33+
protected modalService: ModalService) {
3134
super();
3235
}
36+
37+
showSecondaryModal() {
38+
this.modalService.show({
39+
modalLabel: "Secondary header label",
40+
modalTitle: "Sample secondary modal works.",
41+
modalContent: this.modalText,
42+
buttons: [{
43+
text: "Cancel",
44+
type: ModalButtonType.secondary
45+
}, {
46+
text: "OK",
47+
type: ModalButtonType.primary,
48+
click: () => alert("OK button clicked")
49+
}]
50+
});
51+
}
3352
}
3453

3554
@Component({

src/placeholder/placeholder.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class PlaceholderService {
3030
if (!this.viewContainerRef) {
3131
console.error("No view container defined! Likely due to a missing `ibm-placeholder`");
3232
}
33-
return this.viewContainerRef.createComponent(componentFactory, 0, injector);
33+
return this.viewContainerRef.createComponent(componentFactory, null, injector);
3434
}
3535

3636
destroyComponent(component: ComponentRef<any>) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Component } from "@angular/core";
2+
import { I18n } from "../i18n/i18n.module";
3+
4+
/**
5+
* A slide-out hamburger menu
6+
*
7+
* TODO: This is a stub component, and needs to be implemented.
8+
*/
9+
@Component({
10+
selector: "ibm-hamburger",
11+
template: `
12+
<button
13+
class="bx--header__menu-trigger bx--header__action"
14+
[attr.aria-label]="i18n.get('UI_SHELL.HEADER.MENU') | async"
15+
[attr.title]="i18n.get('UI_SHELL.HEADER.MENU') | async">
16+
<svg aria-hidden="true" width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
17+
<path d="M4 6h24v2H4zm0 18h24v2H4zm0-9h24v2H4z" />
18+
</svg>
19+
</button>
20+
`
21+
})
22+
export class Hamburger {
23+
constructor(public i18n: I18n) { }
24+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { Component } from "@angular/core";
2+
import { ComponentFixture, TestBed } from "@angular/core/testing";
3+
import { By } from "@angular/platform-browser";
4+
5+
import { I18nModule } from "../i18n/i18n.module";
6+
import { Header } from "./header.component";
7+
import { Hamburger } from "./ui-shell.module";
8+
9+
/**
10+
* Testing component for projecting an ibm-hamburger component
11+
* inside of an ibm-header component.
12+
*
13+
* @class HamburgerTest
14+
*/
15+
@Component({
16+
selector: "ibm-hamburger-test",
17+
template: `
18+
<ibm-header>
19+
<ibm-hamburger></ibm-hamburger>
20+
</ibm-header>
21+
`
22+
})
23+
class HamburgerTest { }
24+
25+
describe("UI Shell Header", () => {
26+
let component: Header;
27+
let fixture: ComponentFixture<Header>;
28+
let element: HTMLElement;
29+
30+
beforeEach(() => {
31+
TestBed.configureTestingModule({
32+
declarations: [Hamburger, HamburgerTest, Header],
33+
imports: [I18nModule],
34+
providers: []
35+
});
36+
37+
fixture = TestBed.createComponent(Header);
38+
component = fixture.componentInstance;
39+
element = fixture.debugElement.query(By.css(".bx--header")).nativeElement;
40+
fixture.detectChanges();
41+
});
42+
43+
it("should work", () => {
44+
expect(component instanceof Header).toBe(true);
45+
});
46+
47+
it("should have a default brand of 'IBM'", () => {
48+
const brandElement = element.querySelector(".bx--header__name--prefix");
49+
expect(brandElement).toBeDefined();
50+
expect(brandElement.textContent.trim()).toEqual("IBM");
51+
});
52+
53+
it("should be able to set a custom brand", () => {
54+
const brand = "Foo Brand";
55+
component.brand = brand;
56+
fixture.detectChanges();
57+
58+
const brandElement = element.querySelector(".bx--header__name--prefix");
59+
expect(brandElement).toBeDefined();
60+
expect(brandElement.textContent.trim()).toEqual(brand);
61+
});
62+
63+
it("should be able to set a custom name", () => {
64+
const name = "Foo Name";
65+
component.name = name;
66+
fixture.detectChanges();
67+
68+
const nameElement = element.querySelector(".bx--header__name");
69+
expect(nameElement).toBeDefined();
70+
expect(nameElement.textContent).toContain(name);
71+
});
72+
73+
it("should project a hamburger component", () => {
74+
const headerWithHamburger = TestBed.createComponent(HamburgerTest);
75+
const element = headerWithHamburger.nativeElement;
76+
77+
expect(element.querySelector("header > ibm-hamburger")).toBeDefined();
78+
});
79+
});

src/ui-shell/header.component.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,7 @@ import { I18n } from "./../i18n/i18n.module";
1414
tabindex="0">
1515
{{ i18n.get("UI_SHELL.SKIP_TO") | async }}
1616
</a>
17-
<button
18-
class="bx--header__menu-trigger bx--header__action"
19-
[attr.aria-label]="i18n.get('UI_SHELL.HEADER.MENU') | async"
20-
[attr.title]="i18n.get('UI_SHELL.HEADER.MENU') | async">
21-
<svg aria-hidden="true" width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
22-
<path d="M4 6h24v2H4zm0 18h24v2H4zm0-9h24v2H4z" />
23-
</svg>
24-
</button>
17+
<ng-content select="ibm-hamburger"></ng-content>
2518
<a class="bx--header__name" href="#">
2619
<span class="bx--header__name--prefix">{{brand}}&nbsp;</span>
2720
{{name}}

src/ui-shell/ui-shell.module.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { HeaderNavigation } from "./header-navigation.component";
1010
import { HeaderGlobal } from "./header-global.component";
1111
import { HeaderAction } from "./header-action.component";
1212

13+
import { Hamburger } from "./hamburger.component";
14+
1315
import { SideNav } from "./sidenav.component";
1416
import { SideNavHeader } from "./sidenav-header.component";
1517
import { SideNavItem } from "./sidenav-item.component";
@@ -22,7 +24,7 @@ export {
2224
HeaderNavigation,
2325
HeaderGlobal,
2426
HeaderAction,
25-
27+
Hamburger,
2628
SideNav,
2729
SideNavHeader,
2830
SideNavItem,
@@ -37,25 +39,25 @@ export {
3739
HeaderNavigation,
3840
HeaderGlobal,
3941
HeaderAction,
40-
42+
Hamburger,
4143
SideNav,
4244
SideNavHeader,
4345
SideNavItem,
4446
SideNavMenu
4547
],
46-
imports: [ CommonModule, I18nModule ],
48+
imports: [CommonModule, I18nModule],
4749
exports: [
4850
Header,
4951
HeaderItem,
5052
HeaderMenu,
5153
HeaderNavigation,
5254
HeaderGlobal,
5355
HeaderAction,
54-
56+
Hamburger,
5557
SideNav,
5658
SideNavHeader,
5759
SideNavItem,
5860
SideNavMenu
5961
]
6062
})
61-
export class UIShellModule {}
63+
export class UIShellModule { }

src/ui-shell/ui-shell.stories.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { storiesOf, moduleMetadata } from "@storybook/angular";
2+
import { withKnobs, boolean } from "@storybook/addon-knobs/angular";
23

34
import { UIShellModule } from "./ui-shell.module";
45

@@ -10,9 +11,11 @@ storiesOf("UI Shell", module)
1011
]
1112
})
1213
)
14+
.addDecorator(withKnobs)
1315
.add("Header", () => ({
1416
template: `
15-
<ibm-header (menuClicked)="menuClicked()" name="[Platform]">
17+
<ibm-header name="[Platform]">
18+
<ibm-hamburger *ngIf="hasHamburger"></ibm-hamburger>
1619
<ibm-header-navigation>
1720
<ibm-header-item>Catalog</ibm-header-item>
1821
<ibm-header-item>Docs</ibm-header-item>
@@ -50,6 +53,7 @@ storiesOf("UI Shell", module)
5053
</ibm-header>
5154
`,
5255
props: {
56+
hasHamburger: boolean("Show Hamburger", true),
5357
menuClicked: () => { }
5458
}
5559
}))
@@ -126,7 +130,8 @@ storiesOf("UI Shell", module)
126130
}))
127131
.add("Together", () => ({
128132
template: `
129-
<ibm-header (menuClicked)="menuClicked()" name="[Platform]">
133+
<ibm-header name="[Platform]">
134+
<ibm-hamburger *ngIf="hasHamburger"></ibm-hamburger>
130135
<ibm-header-navigation>
131136
<ibm-header-item>Catalog</ibm-header-item>
132137
<ibm-header-item>Docs</ibm-header-item>
@@ -215,7 +220,7 @@ storiesOf("UI Shell", module)
215220
</ibm-sidenav>
216221
`,
217222
props: {
218-
menuClicked: () => { },
223+
hasHamburger: boolean("Show Hamburger", true),
219224
options: [
220225
{
221226
content: "Option 1",

0 commit comments

Comments
 (0)