Skip to content

Commit 7cd90dd

Browse files
authored
[Fabric] Add Dropdown component (#63)
* [fabric] add dropdown component
1 parent 5a99d70 commit 7cd90dd

File tree

9 files changed

+218
-2
lines changed

9 files changed

+218
-2
lines changed

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
{
22
"cSpell.words": [
33
"Callout",
4+
"Droppable",
45
"Focusable",
56
"Injectable",
67
"Packagr",
78
"Renderable",
9+
"Reselect",
810
"Selectable",
911
"VDOM",
1012
"borderless",

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ <h2>Getting up and running...</h2>
99
</ol>
1010
</div>
1111

12+
<fab-dropdown
13+
[label]="'test label'"
14+
[selectedKey]="selectedItem && selectedItem.key"
15+
[options]="options"
16+
[placeholder]="'select one'"
17+
(onChange)="logEvent('dropdown change', $event)"
18+
(onFocus)="logEvent('dropdown focus', $event)"
19+
(onBlur)="logEvent('dropdown blur', $event)"
20+
></fab-dropdown>
21+
1222
<fab-icon iconName="Add" (onClick)="onClickEventHandler($event)" (onMouseOver)="onMouseOverEventHandler($event)">
1323
</fab-icon>
1424

apps/demo/src/app/app.component.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import { ChangeDetectorRef, Component, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
2-
import { ICalendarStrings, IContextualMenuProps, ISelection, Selection } from 'office-ui-fabric-react';
2+
import {
3+
ICalendarStrings,
4+
IContextualMenuProps,
5+
ISelection,
6+
Selection,
7+
DropdownMenuItemType,
8+
IDropdownOption,
9+
} from 'office-ui-fabric-react';
10+
import { FabDropdownComponent } from '@angular-react/fabric';
311

412
const suffix = ' cm';
513

@@ -23,6 +31,22 @@ export class AppComponent {
2331
console.log('onMouseOver', { ev });
2432
}
2533

34+
logEvent(...args: any[]) {
35+
console.log(args);
36+
}
37+
38+
selectedItem?: IDropdownOption;
39+
options: FabDropdownComponent['options'] = [
40+
{ key: 'A', text: 'Option a' },
41+
{ key: 'B', text: 'Option b' },
42+
{ key: 'C', text: 'Option c' },
43+
{ key: 'D', text: 'Option d' },
44+
{ key: 'divider_1', text: '-', itemType: DropdownMenuItemType.Divider },
45+
{ key: 'E', text: 'Option e' },
46+
{ key: 'F', text: 'Option f' },
47+
{ key: 'G', text: 'Option g' },
48+
];
49+
2650
textFieldValue = 'Hello';
2751

2852
marqueeEnabled: boolean;

apps/demo/src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
FabDialogModule,
1414
FabDividerModule,
1515
FabFabricModule,
16+
FabDropdownModule,
1617
FabGroupModule,
1718
FabGroupedListModule,
1819
FabHoverCardModule,
@@ -49,6 +50,7 @@ import { CounterComponent } from './counter/counter.component';
4950
FabButtonModule,
5051
FabDialogModule,
5152
FabImageModule,
53+
FabDropdownModule,
5254
FabPanelModule,
5355
FabCommandBarModule,
5456
FabBreadcrumbModule,
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { InputRendererOptions, JsxRenderFunc, ReactWrapperComponent } from '@angular-react/core';
5+
import {
6+
ChangeDetectionStrategy,
7+
ChangeDetectorRef,
8+
Component,
9+
ElementRef,
10+
Input,
11+
OnInit,
12+
Renderer2,
13+
ViewChild,
14+
Output,
15+
EventEmitter,
16+
} from '@angular/core';
17+
import { IDropdownProps, IDropdownOption, IDropdown } from 'office-ui-fabric-react/lib/Dropdown';
18+
import { ISelectableDroppableTextProps, ISelectableOption } from 'office-ui-fabric-react';
19+
20+
@Component({
21+
selector: 'fab-dropdown',
22+
exportAs: 'fabDropdown',
23+
template: `
24+
<Dropdown
25+
#reactNode
26+
[componentRef]="componentRef"
27+
[label]="label"
28+
[ariaLabel]="ariaLabel"
29+
[id]="id"
30+
[className]="className"
31+
[defaultSelectedKey]="defaultSelectedKey"
32+
[selectedKey]="selectedKey"
33+
[disabled]="disabled"
34+
[required]="required"
35+
[calloutProps]="calloutProps"
36+
[panelProps]="panelProps"
37+
[errorMessage]="errorMessage"
38+
[placeholder]="placeholder"
39+
[options]="options"
40+
[dropdownWidth]="dropdownWidth"
41+
[responsiveMode]="responsiveMode"
42+
[multiSelect]="multiSelect"
43+
[defaultSelectedKeys]="defaultSelectedKeys"
44+
[selectedKeys]="selectedKeys"
45+
[multiSelectDelimiter]="multiSelectDelimiter"
46+
[notifyOnReselect]="notifyOnReselect"
47+
[keytipProps]="keytipProps"
48+
[theme]="theme"
49+
[styles]="styles"
50+
[RenderContainer]="renderContainer && onRenderContainer"
51+
[RenderList]="renderList && onRenderList"
52+
[RenderItem]="renderItem && onRenderItem"
53+
[RenderOption]="renderOption && onRenderOption"
54+
[RenderPlaceHolder]="renderPlaceHolder && onRenderPlaceHolder"
55+
[RenderTitle]="renderTitle && onRenderTitle"
56+
[RenderCaretDown]="renderCaretDown && onRenderCaretDown"
57+
[Change]="onChangeHandler"
58+
[Dismiss]="onDismissHandler"
59+
></Dropdown>
60+
`,
61+
styles: ['react-renderer'],
62+
changeDetection: ChangeDetectionStrategy.OnPush,
63+
})
64+
export class FabDropdownComponent extends ReactWrapperComponent<IDropdownProps> implements OnInit {
65+
@ViewChild('reactNode') protected reactNodeRef: ElementRef;
66+
67+
@Input() componentRef?: IDropdownProps['componentRef'];
68+
@Input() label?: IDropdownProps['label'];
69+
@Input() ariaLabel?: IDropdownProps['ariaLabel'];
70+
@Input() id?: IDropdownProps['id'];
71+
@Input() className?: IDropdownProps['className'];
72+
@Input() defaultSelectedKey?: IDropdownProps['defaultSelectedKey'];
73+
@Input() selectedKey?: IDropdownProps['selectedKey'];
74+
@Input() disabled?: IDropdownProps['disabled'];
75+
@Input() required?: IDropdownProps['required'];
76+
@Input() calloutProps?: IDropdownProps['calloutProps'];
77+
@Input() panelProps?: IDropdownProps['panelProps'];
78+
@Input() errorMessage?: IDropdownProps['errorMessage'];
79+
80+
@Input() placeholder: IDropdownProps['placeholder'];
81+
@Input() options: IDropdownProps['options'];
82+
@Input() dropdownWidth?: IDropdownProps['dropdownWidth'];
83+
@Input() responsiveMode?: IDropdownProps['responsiveMode'];
84+
@Input() multiSelect?: IDropdownProps['multiSelect'];
85+
@Input() defaultSelectedKeys?: IDropdownProps['defaultSelectedKeys'];
86+
@Input() selectedKeys?: IDropdownProps['selectedKeys'];
87+
@Input() multiSelectDelimiter?: IDropdownProps['multiSelectDelimiter'];
88+
@Input() notifyOnReselect?: IDropdownProps['notifyOnReselect'];
89+
@Input() keytipProps?: IDropdownProps['keytipProps'];
90+
@Input() theme?: IDropdownProps['theme'];
91+
@Input() styles?: IDropdownProps['styles'];
92+
93+
@Input() renderContainer?: InputRendererOptions<ISelectableDroppableTextProps<IDropdown>>;
94+
@Input() renderList?: InputRendererOptions<ISelectableDroppableTextProps<IDropdown>>;
95+
@Input() renderItem?: InputRendererOptions<ISelectableOption>;
96+
@Input() renderOption?: InputRendererOptions<ISelectableOption>;
97+
@Input() renderPlaceHolder?: InputRendererOptions<IDropdownProps>;
98+
@Input() renderTitle?: InputRendererOptions<IDropdownOption | IDropdownOption[]>;
99+
@Input() renderCaretDown?: InputRendererOptions<IDropdownProps>;
100+
101+
@Output() readonly onChange = new EventEmitter<{ event: Event; option?: IDropdownOption; index?: number }>();
102+
@Output() readonly onDismiss = new EventEmitter<void>();
103+
104+
onRenderContainer: (
105+
props?: ISelectableDroppableTextProps<IDropdown, IDropdown>,
106+
defaultRender?: JsxRenderFunc<ISelectableDroppableTextProps<IDropdown, IDropdown>>
107+
) => JSX.Element;
108+
onRenderList: (
109+
props?: ISelectableDroppableTextProps<IDropdown, IDropdown>,
110+
defaultRender?: JsxRenderFunc<ISelectableDroppableTextProps<IDropdown, IDropdown>>
111+
) => JSX.Element;
112+
onRenderItem: (props?: ISelectableOption, defaultRender?: JsxRenderFunc<ISelectableOption>) => JSX.Element;
113+
onRenderOption: (props?: ISelectableOption, defaultRender?: JsxRenderFunc<ISelectableOption>) => JSX.Element;
114+
onRenderPlaceHolder: (props?: IDropdownProps, defaultRender?: JsxRenderFunc<IDropdownProps>) => JSX.Element;
115+
onRenderTitle: (
116+
props?: IDropdownOption | IDropdownOption[],
117+
defaultRender?: JsxRenderFunc<IDropdownOption | IDropdownOption[]>
118+
) => JSX.Element;
119+
onRenderCaretDown: (props?: IDropdownProps, defaultRender?: JsxRenderFunc<IDropdownProps>) => JSX.Element;
120+
121+
constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) {
122+
super(elementRef, changeDetectorRef, renderer, { setHostDisplay: true });
123+
124+
this.onChangeHandler = this.onChangeHandler.bind(this);
125+
this.onDismissHandler = this.onDismissHandler.bind(this);
126+
}
127+
128+
ngOnInit() {
129+
this.onRenderContainer = this.createRenderPropHandler(this.renderContainer);
130+
this.onRenderList = this.createRenderPropHandler(this.renderList);
131+
this.onRenderItem = this.createRenderPropHandler(this.renderItem);
132+
this.onRenderOption = this.createRenderPropHandler(this.renderOption);
133+
this.onRenderPlaceHolder = this.createRenderPropHandler(this.renderPlaceHolder);
134+
this.onRenderTitle = this.createRenderPropHandler(this.renderTitle);
135+
this.onRenderCaretDown = this.createRenderPropHandler(this.renderCaretDown);
136+
}
137+
138+
onChangeHandler(event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number) {
139+
this.onChange.emit({
140+
event: event && event.nativeEvent,
141+
option,
142+
index,
143+
});
144+
}
145+
146+
onDismissHandler() {
147+
this.onDismiss.emit();
148+
}
149+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { registerElement } from '@angular-react/core';
5+
import { CommonModule } from '@angular/common';
6+
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
7+
import { Dropdown } from 'office-ui-fabric-react/lib/Dropdown';
8+
import { FabDropdownComponent } from './dropdown.component';
9+
10+
const components = [FabDropdownComponent];
11+
12+
@NgModule({
13+
imports: [CommonModule],
14+
declarations: components,
15+
exports: components,
16+
schemas: [NO_ERRORS_SCHEMA],
17+
})
18+
export class FabDropdownModule {
19+
constructor() {
20+
// Add any React elements to the registry (used by the renderer).
21+
registerElement('Dropdown', () => Dropdown);
22+
}
23+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
export * from './dropdown.component';
5+
export * from './dropdown.module';

libs/fabric/src/lib/components/text-field/base-text-field.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent<ITextFieldP
6767
onRenderSuffix: (props?: ITextFieldProps, defaultRender?: JsxRenderFunc<ITextFieldProps>) => JSX.Element;
6868

6969
constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2) {
70-
super(elementRef, changeDetectorRef, renderer);
70+
super(elementRef, changeDetectorRef, renderer, { setHostDisplay: true });
7171

7272
this.onChangeHandler = this.onChangeHandler.bind(this);
7373
this.onBeforeChangeHandler = this.onBeforeChangeHandler.bind(this);

libs/fabric/src/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from './lib/components/date-picker/public-api';
1414
export * from './lib/components/details-list/public-api';
1515
export * from './lib/components/dialog/public-api';
1616
export * from './lib/components/divider/public-api';
17+
export * from './lib/components/dropdown/public-api';
1718
export * from './lib/components/fabric/public-api';
1819
export * from './lib/components/group/public-api';
1920
export * from './lib/components/grouped-list/public-api';

0 commit comments

Comments
 (0)