Skip to content

Commit 8ccbaa5

Browse files
reshawbengry
authored andcommitted
Update combobox and calendar with directives (#95)
Add directives for the ComboBox and Calendar to enable the Angular-style template localization pattern. Change wrapper to fix some timing bugs
1 parent 87863d9 commit 8ccbaa5

File tree

15 files changed

+255
-17
lines changed

15 files changed

+255
-17
lines changed

apps/demo/src/app/app.component.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,30 @@ <h2>Getting up and running...</h2>
142142

143143
<fab-calendar [strings]="strings" (onSelectDate)="onSelectDate($event)"></fab-calendar>
144144

145+
<fab-calendar (onSelectDate)="onSelectDate($event)">
146+
<fab-calendar-strings
147+
[months]="[
148+
'January',
149+
'Fabruary',
150+
'March',
151+
'April',
152+
'May',
153+
'June',
154+
'July',
155+
'August',
156+
'September',
157+
'October',
158+
'November',
159+
'December'
160+
]"
161+
[shortMonths]="['Jan', 'Fab', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']"
162+
[days]="['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']"
163+
[shortDays]="['S', 'M', 'T', 'W', 'T', 'F', 'S']"
164+
goToToday="Go to tomorrow"
165+
weekNumberFormatString="Week number {0}"
166+
></fab-calendar-strings>
167+
</fab-calendar>
168+
145169
<fab-marquee-selection [isEnabled]="marqueeEnabled" [selection]="selection">
146170
<fab-details-list
147171
[selection]="selection"

apps/docs/src/app/containers/component-docs/fabric/fabric.component.html

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<h1>Microsoft Fabric [React] Components</h1>
22

33
<h2>Button</h2>
4-
<fab-default-button text="Toggle Disabled" (onClick)="toggle()"></fab-default-button>
4+
<fab-default-button text="Toggle Disabled" (onClick)="toggle()" contentStyle="margin-right: 10px;"></fab-default-button>
55
<fab-default-button
66
[disabled]="disabled"
77
(onClick)="click()"
@@ -22,7 +22,46 @@ <h2>Dialog</h2>
2222
>
2323
{{ sampleContent2 }} {{ sampleContent3 }}
2424
<fab-dialog-footer>
25-
<fab-default-button (onClick)="clickSave()" text="Save" contentStyle="margin-right: 10px;"></fab-default-button>
25+
<fab-default-button (onClick)="clickSave()" text="Save"></fab-default-button>
2626
<fab-default-button (onClick)="toggleDialog()" text="Cancel"></fab-default-button>
2727
</fab-dialog-footer>
2828
</fab-dialog>
29+
30+
<hr />
31+
32+
<h2>Combo box</h2>
33+
<fab-combo-box contentStyle="width: 300px;" (onChange)="comboChange($event)">
34+
<options>
35+
<fab-combo-box-option optionKey="A" text="See option A"></fab-combo-box-option>
36+
<fab-combo-box-option optionKey="B" text="See option B"></fab-combo-box-option>
37+
</options>
38+
</fab-combo-box>
39+
{{ selectedComboBoxKey }}: {{ selectedComboBoxValue }}
40+
41+
<hr />
42+
43+
<h2>Directive Calendar</h2>
44+
<fab-calendar (onSelectDate)="onSelectDate($event)">
45+
<fab-calendar-strings
46+
[months]="[
47+
'January',
48+
'Fabruary',
49+
'March',
50+
'April',
51+
'May',
52+
'June',
53+
'July',
54+
'August',
55+
'September',
56+
'October',
57+
'November',
58+
'December'
59+
]"
60+
[shortMonths]="['Jan', 'Fab', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']"
61+
[days]="['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']"
62+
[shortDays]="['S', 'M', 'T', 'W', 'T', 'F', 'S']"
63+
goToToday="Go to tomorrow"
64+
weekNumberFormatString="Week number {0}"
65+
></fab-calendar-strings>
66+
</fab-calendar>
67+
{{ selectedDate }}

apps/docs/src/app/containers/component-docs/fabric/fabric.component.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Component } from '@angular/core';
2+
import { IComboBoxOption, ICalendarStrings } from 'office-ui-fabric-react';
23

34
@Component({
45
selector: 'app-fabric',
@@ -12,6 +13,23 @@ export class FabricComponent {
1213
secondsCounter = 0;
1314
sampleContent2 = '0 Seconds Passed';
1415
sampleContent3 = '';
16+
selectedComboBoxKey: string = "None";
17+
selectedComboBoxValue: string = "None";
18+
selectedDate: Date;
19+
20+
comboBoxOptions: IComboBoxOption[] = [
21+
{ key: 'A', text: 'See option A' },
22+
{ key: 'B', text: 'See option B' },
23+
];
24+
25+
onSelectDate(event) {
26+
this.selectedDate = event.date;
27+
}
28+
29+
comboChange(event) {
30+
this.selectedComboBoxKey = event.option.key;
31+
this.selectedComboBoxValue = event.option.text;
32+
}
1533

1634
get sampleContent() {
1735
return `Button clicked ${this.sampleContentCounter} times.`;

apps/docs/src/app/fabric.module.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import { NgModule } from '@angular/core';
2-
import { FabButtonModule, FabDialogModule } from '@angular-react/fabric';
2+
import {
3+
FabButtonModule,
4+
FabDialogModule,
5+
FabComboBoxModule,
6+
FabCalendarModule
7+
} from '@angular-react/fabric';
38

4-
const componentModules = [FabButtonModule, FabDialogModule];
9+
const componentModules = [
10+
FabButtonModule,
11+
FabDialogModule,
12+
FabComboBoxModule,
13+
FabCalendarModule
14+
];
515
@NgModule({
616
imports: componentModules,
717
exports: componentModules,

libs/core/src/lib/components/wrapper-component.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
9797
this._contentClass = value;
9898
if (isReactNode(this.reactNodeRef.nativeElement)) {
9999
this.reactNodeRef.nativeElement.setProperty('className', classnames(value));
100-
this.changeDetectorRef.detectChanges();
100+
this.markForCheck();
101101
}
102102
}
103103

@@ -118,7 +118,7 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
118118
if (isReactNode(this.reactNodeRef.nativeElement)) {
119119
const stringValue = typeof value === 'string' ? value : stylenames(value);
120120
this.reactNodeRef.nativeElement.setProperty('style', toStyle(stringValue));
121-
this.changeDetectorRef.detectChanges();
121+
this.markForCheck();
122122
}
123123
}
124124

@@ -142,9 +142,11 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
142142
this._shouldSetHostDisplay = setHostDisplay;
143143
}
144144

145-
ngAfterViewInit() {
145+
ngAfterContentInit() {
146146
this._passAttributesAsProps();
147+
}
147148

149+
ngAfterViewInit() {
148150
if (this._shouldSetHostDisplay) {
149151
this._setHostDisplay();
150152
}

libs/fabric/src/lib/components/button/base-button.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent<IButt
129129

130130
setItems(this.menuItemsDirectives.toArray());
131131
}
132+
super.ngAfterContentInit();
132133
}
133134

134135
ngOnDestroy() {

libs/fabric/src/lib/components/calendar/calendar.component.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ import {
1212
Output,
1313
Renderer2,
1414
ViewChild,
15+
ContentChild,
16+
AfterContentInit,
1517
} from '@angular/core';
1618
import { ICalendarProps } from 'office-ui-fabric-react/lib/Calendar';
19+
import { CalendarStringsDirective } from './directives/calendar-strings-directive.component';
1720

1821
@Component({
1922
selector: 'fab-calendar',
@@ -57,7 +60,7 @@ import { ICalendarProps } from 'office-ui-fabric-react/lib/Calendar';
5760
styles: ['react-renderer'],
5861
changeDetection: ChangeDetectionStrategy.OnPush,
5962
})
60-
export class FabCalendarComponent extends ReactWrapperComponent<ICalendarProps> {
63+
export class FabCalendarComponent extends ReactWrapperComponent<ICalendarProps> implements AfterContentInit {
6164
@ViewChild('reactNode') protected reactNodeRef: ElementRef;
6265

6366
@Input() componentRef?: ICalendarProps['componentRef'];
@@ -91,14 +94,22 @@ export class FabCalendarComponent extends ReactWrapperComponent<ICalendarProps>
9194
@Output() readonly onSelectDate = new EventEmitter<{ date: Date; selectedDateRangeArray?: Date[] }>();
9295
@Output() readonly onDismiss = new EventEmitter<void>();
9396

97+
@ContentChild(CalendarStringsDirective) readonly calendarStringsDirective?: CalendarStringsDirective;
98+
9499
constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) {
95100
super(elementRef, changeDetectorRef, renderer);
96-
97101
// coming from React context - we need to bind to this so we can access the Angular Component properties
98102
this.onSelectDateHandler = this.onSelectDateHandler.bind(this);
99103
this.onDismissHandler = this.onDismissHandler.bind(this);
100104
}
101105

106+
ngAfterContentInit() {
107+
if (this.calendarStringsDirective) {
108+
this._initDirective(this.calendarStringsDirective);
109+
super.ngAfterContentInit();
110+
}
111+
}
112+
102113
onSelectDateHandler(date: Date, selectedDateRangeArray?: Date[]) {
103114
this.onSelectDate.emit({
104115
date,
@@ -109,4 +120,8 @@ export class FabCalendarComponent extends ReactWrapperComponent<ICalendarProps>
109120
onDismissHandler() {
110121
this.onDismiss.emit();
111122
}
123+
124+
private _initDirective(calendarStringsDirective: CalendarStringsDirective) {
125+
this.strings = calendarStringsDirective.strings;
126+
}
112127
}

libs/fabric/src/lib/components/calendar/calendar.module.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ import * as CalendarCss from 'office-ui-fabric-react/lib-amd/components/Calendar
88
import { Calendar } from 'office-ui-fabric-react';
99
import { noop } from '../../utils/noop';
1010
import { FabCalendarComponent } from './calendar.component';
11+
import { CalendarStringsDirective } from './directives/calendar-strings-directive.component';
1112

1213
// Dummy action to force CalendarCss to load and not be tree-shaken away.
1314
noop(CalendarCss);
1415

15-
const components = [FabCalendarComponent];
16+
const declarations = [FabCalendarComponent, CalendarStringsDirective];
1617

1718
@NgModule({
1819
imports: [CommonModule],
19-
declarations: components,
20-
exports: components,
20+
declarations: declarations,
21+
exports: declarations,
2122
schemas: [NO_ERRORS_SCHEMA],
2223
})
2324
export class FabCalendarModule {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { Directive, Input } from '@angular/core';
5+
import { ICalendarStrings } from 'office-ui-fabric-react';
6+
7+
/**
8+
* Wrapper directive for calendar strings
9+
*/
10+
@Directive({ selector: 'fab-calendar > fab-calendar-strings' })
11+
export class CalendarStringsDirective {
12+
13+
@Input() months: ICalendarStrings['months'];
14+
@Input() shortMonths: ICalendarStrings['shortMonths'];
15+
@Input() days: ICalendarStrings['days'];
16+
@Input() shortDays: ICalendarStrings['shortDays'];
17+
@Input() goToToday: ICalendarStrings['goToToday'];
18+
@Input() weekNumberFormatString: ICalendarStrings['weekNumberFormatString'];
19+
20+
21+
get strings(): ICalendarStrings {
22+
return {
23+
months: this.months,
24+
shortMonths: this.shortMonths,
25+
days: this.days,
26+
shortDays: this.shortDays,
27+
goToToday: this.goToToday,
28+
weekNumberFormatString: this.weekNumberFormatString
29+
}
30+
}
31+
}

libs/fabric/src/lib/components/combo-box/base-combo-box.component.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,26 @@
22
// Licensed under the MIT License.
33

44
import { InputRendererOptions, JsxRenderFunc, ReactWrapperComponent } from '@angular-react/core';
5-
import { ChangeDetectorRef, ElementRef, EventEmitter, Input, NgZone, OnInit, Output, Renderer2 } from '@angular/core';
5+
import {
6+
ChangeDetectorRef,
7+
ElementRef,
8+
EventEmitter,
9+
Input,
10+
NgZone,
11+
OnInit,
12+
Output,
13+
Renderer2,
14+
ContentChild,
15+
AfterContentInit,
16+
} from '@angular/core';
617
import { IComboBox, IComboBoxOption, IComboBoxProps } from 'office-ui-fabric-react/lib/ComboBox';
18+
import { ComboBoxOptionDirective } from './directives/combo-box-option.directive';
19+
import { ComboBoxOptionsDirective } from './directives/combo-box-options.directive';
20+
21+
export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent<IComboBoxProps>
22+
implements OnInit, AfterContentInit {
23+
@ContentChild(ComboBoxOptionDirective) readonly optionsDirective?: ComboBoxOptionDirective;
724

8-
export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent<IComboBoxProps> implements OnInit {
925
@Input() componentRef?: IComboBoxProps['componentRef'];
1026
@Input() options: IComboBoxProps['options'];
1127
@Input() allowFreeform?: IComboBoxProps['allowFreeform'];
@@ -49,6 +65,8 @@ export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent<ICo
4965
@Output() readonly onMenuDismissed = new EventEmitter<void>();
5066
@Output() readonly onScrollToItem = new EventEmitter<{ itemIndex: number }>();
5167

68+
@ContentChild(ComboBoxOptionsDirective) readonly comboBoxOptionsDirective?: ComboBoxOptionsDirective;
69+
5270
onRenderLowerContent: (props?: IComboBoxProps, defaultRender?: JsxRenderFunc<IComboBoxProps>) => JSX.Element;
5371

5472
constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2, ngZone: NgZone) {
@@ -65,6 +83,13 @@ export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent<ICo
6583
this.onRenderLowerContent = this.createRenderPropHandler(this.renderLowerContent);
6684
}
6785

86+
ngAfterContentInit() {
87+
if (this.comboBoxOptionsDirective) {
88+
this._initDirective(this.comboBoxOptionsDirective);
89+
}
90+
super.ngAfterContentInit();
91+
}
92+
6893
onItemClickHandler(event: React.FormEvent<IComboBox>, option?: IComboBoxOption, index?: number) {
6994
this.onItemClick.emit({
7095
event: event.nativeEvent,
@@ -95,4 +120,9 @@ export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent<ICo
95120
itemIndex,
96121
});
97122
}
123+
124+
private _initDirective(directive: ComboBoxOptionsDirective) {
125+
this.options = directive.items;
126+
this.markForCheck();
127+
}
98128
}

0 commit comments

Comments
 (0)