diff --git a/apps/demo/src/app/app.component.html b/apps/demo/src/app/app.component.html index ff0bdb5e..77fbc103 100644 --- a/apps/demo/src/app/app.component.html +++ b/apps/demo/src/app/app.component.html @@ -142,6 +142,30 @@

Getting up and running...

+ + + + Microsoft Fabric [React] Components

Button

- + Dialog > {{ sampleContent2 }} {{ sampleContent3 }} - + + +
+ +

Combo box

+ + + + + + +{{ selectedComboBoxKey }}: {{ selectedComboBoxValue }} + +
+ +

Directive Calendar

+ + + +{{ selectedDate }} diff --git a/apps/docs/src/app/containers/component-docs/fabric/fabric.component.ts b/apps/docs/src/app/containers/component-docs/fabric/fabric.component.ts index bf39cc9f..6109e152 100644 --- a/apps/docs/src/app/containers/component-docs/fabric/fabric.component.ts +++ b/apps/docs/src/app/containers/component-docs/fabric/fabric.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { IComboBoxOption, ICalendarStrings } from 'office-ui-fabric-react'; @Component({ selector: 'app-fabric', @@ -12,6 +13,23 @@ export class FabricComponent { secondsCounter = 0; sampleContent2 = '0 Seconds Passed'; sampleContent3 = ''; + selectedComboBoxKey: string = "None"; + selectedComboBoxValue: string = "None"; + selectedDate: Date; + + comboBoxOptions: IComboBoxOption[] = [ + { key: 'A', text: 'See option A' }, + { key: 'B', text: 'See option B' }, + ]; + + onSelectDate(event) { + this.selectedDate = event.date; + } + + comboChange(event) { + this.selectedComboBoxKey = event.option.key; + this.selectedComboBoxValue = event.option.text; + } get sampleContent() { return `Button clicked ${this.sampleContentCounter} times.`; diff --git a/apps/docs/src/app/fabric.module.ts b/apps/docs/src/app/fabric.module.ts index 42c601bb..dafe848d 100644 --- a/apps/docs/src/app/fabric.module.ts +++ b/apps/docs/src/app/fabric.module.ts @@ -1,7 +1,17 @@ import { NgModule } from '@angular/core'; -import { FabButtonModule, FabDialogModule } from '@angular-react/fabric'; +import { + FabButtonModule, + FabDialogModule, + FabComboBoxModule, + FabCalendarModule +} from '@angular-react/fabric'; -const componentModules = [FabButtonModule, FabDialogModule]; +const componentModules = [ + FabButtonModule, + FabDialogModule, + FabComboBoxModule, + FabCalendarModule +]; @NgModule({ imports: componentModules, exports: componentModules, diff --git a/libs/core/src/lib/components/wrapper-component.ts b/libs/core/src/lib/components/wrapper-component.ts index 77accbf0..0dc6e3e1 100644 --- a/libs/core/src/lib/components/wrapper-component.ts +++ b/libs/core/src/lib/components/wrapper-component.ts @@ -97,7 +97,7 @@ export abstract class ReactWrapperComponent implements AfterV this._contentClass = value; if (isReactNode(this.reactNodeRef.nativeElement)) { this.reactNodeRef.nativeElement.setProperty('className', classnames(value)); - this.changeDetectorRef.detectChanges(); + this.markForCheck(); } } @@ -118,7 +118,7 @@ export abstract class ReactWrapperComponent implements AfterV if (isReactNode(this.reactNodeRef.nativeElement)) { const stringValue = typeof value === 'string' ? value : stylenames(value); this.reactNodeRef.nativeElement.setProperty('style', toStyle(stringValue)); - this.changeDetectorRef.detectChanges(); + this.markForCheck(); } } @@ -142,9 +142,11 @@ export abstract class ReactWrapperComponent implements AfterV this._shouldSetHostDisplay = setHostDisplay; } - ngAfterViewInit() { + ngAfterContentInit() { this._passAttributesAsProps(); + } + ngAfterViewInit() { if (this._shouldSetHostDisplay) { this._setHostDisplay(); } diff --git a/libs/fabric/src/lib/components/button/base-button.component.ts b/libs/fabric/src/lib/components/button/base-button.component.ts index a6977cbc..e43cd76d 100644 --- a/libs/fabric/src/lib/components/button/base-button.component.ts +++ b/libs/fabric/src/lib/components/button/base-button.component.ts @@ -129,6 +129,7 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent { +export class FabCalendarComponent extends ReactWrapperComponent implements AfterContentInit { @ViewChild('reactNode') protected reactNodeRef: ElementRef; @Input() componentRef?: ICalendarProps['componentRef']; @@ -91,14 +94,22 @@ export class FabCalendarComponent extends ReactWrapperComponent @Output() readonly onSelectDate = new EventEmitter<{ date: Date; selectedDateRangeArray?: Date[] }>(); @Output() readonly onDismiss = new EventEmitter(); + @ContentChild(CalendarStringsDirective) readonly calendarStringsDirective?: CalendarStringsDirective; + constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) { super(elementRef, changeDetectorRef, renderer); - // coming from React context - we need to bind to this so we can access the Angular Component properties this.onSelectDateHandler = this.onSelectDateHandler.bind(this); this.onDismissHandler = this.onDismissHandler.bind(this); } + ngAfterContentInit() { + if (this.calendarStringsDirective) { + this._initDirective(this.calendarStringsDirective); + super.ngAfterContentInit(); + } + } + onSelectDateHandler(date: Date, selectedDateRangeArray?: Date[]) { this.onSelectDate.emit({ date, @@ -109,4 +120,8 @@ export class FabCalendarComponent extends ReactWrapperComponent onDismissHandler() { this.onDismiss.emit(); } + + private _initDirective(calendarStringsDirective: CalendarStringsDirective) { + this.strings = calendarStringsDirective.strings; + } } diff --git a/libs/fabric/src/lib/components/calendar/calendar.module.ts b/libs/fabric/src/lib/components/calendar/calendar.module.ts index 32b55ab7..1aaacfbd 100644 --- a/libs/fabric/src/lib/components/calendar/calendar.module.ts +++ b/libs/fabric/src/lib/components/calendar/calendar.module.ts @@ -8,16 +8,17 @@ import * as CalendarCss from 'office-ui-fabric-react/lib-amd/components/Calendar import { Calendar } from 'office-ui-fabric-react'; import { noop } from '../../utils/noop'; import { FabCalendarComponent } from './calendar.component'; +import { CalendarStringsDirective } from './directives/calendar-strings-directive.component'; // Dummy action to force CalendarCss to load and not be tree-shaken away. noop(CalendarCss); -const components = [FabCalendarComponent]; +const declarations = [FabCalendarComponent, CalendarStringsDirective]; @NgModule({ imports: [CommonModule], - declarations: components, - exports: components, + declarations: declarations, + exports: declarations, schemas: [NO_ERRORS_SCHEMA], }) export class FabCalendarModule { diff --git a/libs/fabric/src/lib/components/calendar/directives/calendar-strings-directive.component.ts b/libs/fabric/src/lib/components/calendar/directives/calendar-strings-directive.component.ts new file mode 100644 index 00000000..70ac7c7f --- /dev/null +++ b/libs/fabric/src/lib/components/calendar/directives/calendar-strings-directive.component.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { Directive, Input } from '@angular/core'; +import { ICalendarStrings } from 'office-ui-fabric-react'; + +/** + * Wrapper directive for calendar strings + */ +@Directive({ selector: 'fab-calendar > fab-calendar-strings' }) +export class CalendarStringsDirective { + + @Input() months: ICalendarStrings['months']; + @Input() shortMonths: ICalendarStrings['shortMonths']; + @Input() days: ICalendarStrings['days']; + @Input() shortDays: ICalendarStrings['shortDays']; + @Input() goToToday: ICalendarStrings['goToToday']; + @Input() weekNumberFormatString: ICalendarStrings['weekNumberFormatString']; + + + get strings(): ICalendarStrings { + return { + months: this.months, + shortMonths: this.shortMonths, + days: this.days, + shortDays: this.shortDays, + goToToday: this.goToToday, + weekNumberFormatString: this.weekNumberFormatString + } + } +} diff --git a/libs/fabric/src/lib/components/combo-box/base-combo-box.component.ts b/libs/fabric/src/lib/components/combo-box/base-combo-box.component.ts index 435bae06..18ef0823 100644 --- a/libs/fabric/src/lib/components/combo-box/base-combo-box.component.ts +++ b/libs/fabric/src/lib/components/combo-box/base-combo-box.component.ts @@ -2,10 +2,26 @@ // Licensed under the MIT License. import { InputRendererOptions, JsxRenderFunc, ReactWrapperComponent } from '@angular-react/core'; -import { ChangeDetectorRef, ElementRef, EventEmitter, Input, NgZone, OnInit, Output, Renderer2 } from '@angular/core'; +import { + ChangeDetectorRef, + ElementRef, + EventEmitter, + Input, + NgZone, + OnInit, + Output, + Renderer2, + ContentChild, + AfterContentInit, +} from '@angular/core'; import { IComboBox, IComboBoxOption, IComboBoxProps } from 'office-ui-fabric-react/lib/ComboBox'; +import { ComboBoxOptionDirective } from './directives/combo-box-option.directive'; +import { ComboBoxOptionsDirective } from './directives/combo-box-options.directive'; + +export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent + implements OnInit, AfterContentInit { + @ContentChild(ComboBoxOptionDirective) readonly optionsDirective?: ComboBoxOptionDirective; -export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent implements OnInit { @Input() componentRef?: IComboBoxProps['componentRef']; @Input() options: IComboBoxProps['options']; @Input() allowFreeform?: IComboBoxProps['allowFreeform']; @@ -49,6 +65,8 @@ export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent(); @Output() readonly onScrollToItem = new EventEmitter<{ itemIndex: number }>(); + @ContentChild(ComboBoxOptionsDirective) readonly comboBoxOptionsDirective?: ComboBoxOptionsDirective; + onRenderLowerContent: (props?: IComboBoxProps, defaultRender?: JsxRenderFunc) => JSX.Element; constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2, ngZone: NgZone) { @@ -65,6 +83,13 @@ export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent, option?: IComboBoxOption, index?: number) { this.onItemClick.emit({ event: event.nativeEvent, @@ -95,4 +120,9 @@ export abstract class FabBaseComboBoxComponent extends ReactWrapperComponent options' }) +export class ComboBoxOptionsDirective { + @ContentChildren(ComboBoxOptionDirective) readonly directiveItems: QueryList; + + get items() { + return this.directiveItems.map(directiveItem => { + return { + key: directiveItem.optionKey, + text: directiveItem.text, + title: directiveItem.title, + itemType: directiveItem.itemType, + index: directiveItem.index, + ariaLabel: directiveItem.ariaLabel, + selected: directiveItem.selected, + disabled: directiveItem.disabled, + data: directiveItem.data, + styles: directiveItem.styles, + useAriaLabelAsText: directiveItem.useAriaLabelAsText + } + }); + } +} diff --git a/libs/fabric/src/lib/components/command-bar/command-bar.component.ts b/libs/fabric/src/lib/components/command-bar/command-bar.component.ts index 48f4dbea..3c5358a6 100644 --- a/libs/fabric/src/lib/components/command-bar/command-bar.component.ts +++ b/libs/fabric/src/lib/components/command-bar/command-bar.component.ts @@ -127,6 +127,7 @@ export class FabCommandBarComponent extends ReactWrapperComponent