Skip to content

Commit 2896b73

Browse files
author
Ben Grynhaus
committed
SearchBox component
1 parent 20739fa commit 2896b73

File tree

6 files changed

+186
-2
lines changed

6 files changed

+186
-2
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ <h2>Getting up and running...</h2>
2727
noResultsFoundText: 'No Color Tags Found'
2828
}" [itemLimit]="2"></fab-tag-picker>
2929
</fab-fabric>
30-
</div>
30+
31+
<fab-search-box
32+
placeholder="Search">
33+
</fab-search-box>
34+
</div>

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AngularReactBrowserModule } from '@angular-react/core';
2-
import { FabBreadcrumbModule, FabButtonModule, FabCalloutModule, FabModalModule, FabCheckboxModule, FabChoiceGroupModule, FabComboBoxModule, FabCommandBarModule, FabDatePickerModule, FabDialogModule, FabDividerModule, FabFabricModule, FabGroupedListModule, FabIconModule, FabImageModule, FabPanelModule, FabPersonaModule, FabSpinnerModule, FabMessageBarModule, FabLinkModule, FabToggleModule, FabPivotModule, FabHoverCardModule, FabTooltipModule, FabShimmerModule, FabSliderModule } from '@angular-react/fabric';
2+
import { FabBreadcrumbModule, FabButtonModule, FabCalloutModule, FabModalModule, FabCheckboxModule, FabChoiceGroupModule, FabComboBoxModule, FabCommandBarModule, FabDatePickerModule, FabDialogModule, FabDividerModule, FabFabricModule, FabGroupedListModule, FabIconModule, FabImageModule, FabPanelModule, FabPersonaModule, FabSpinnerModule, FabMessageBarModule, FabLinkModule, FabToggleModule, FabPivotModule, FabHoverCardModule, FabTooltipModule, FabShimmerModule, FabSliderModule, FabSearchBoxModule } from '@angular-react/fabric';
33
import { NgModule } from '@angular/core';
44
import { NxModule } from '@nrwl/nx';
55
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons';
@@ -37,6 +37,7 @@ import { FabTagPickerModule } from '@angular-react/fabric/src/components/pickers
3737
FabShimmerModule,
3838
FabSliderModule,
3939
FabTagPickerModule,
40+
FabSearchBoxModule,
4041
],
4142
declarations: [AppComponent, PanelBodyComponent],
4243
bootstrap: [AppComponent],
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './public-api';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './search-box.component';
2+
export * from './search-box.module';
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { ReactWrapperComponent, InputRendererOptions } from '@angular-react/core';
2+
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, Output, ViewChild, OnInit } from '@angular/core';
3+
import { ISearchBoxProps } from 'office-ui-fabric-react/lib/components/SearchBox';
4+
import { IButtonProps } from 'office-ui-fabric-react/lib/components/Button';
5+
import { IContextualMenuProps } from 'office-ui-fabric-react/lib/components/ContextualMenu';
6+
import omit from '../../utils/omit';
7+
8+
@Component({
9+
selector: 'fab-search-box',
10+
exportAs: 'fabSearchBox',
11+
template: `
12+
<SearchBox
13+
#reactNode
14+
[componentRef]="componentRef"
15+
[placeholder]="placeholder"
16+
[labelText]="labelText"
17+
[value]="value"
18+
[defaultValue]="defaultValue"
19+
[className]="className"
20+
[ariaLabel]="ariaLabel"
21+
[clearButtonProps]="clearButtonProps"
22+
[underlined]="underlined"
23+
[theme]="theme"
24+
[styles]="styles"
25+
[disableAnimation]="disableAnimation"
26+
[Change]="onChangeHandler"
27+
[Search]="onSearchHandler"
28+
[Clear]="onClearHandler"
29+
[Escape]="onEscapeHandler"
30+
[Changed]="onChangedHandler">
31+
</SearchBox>
32+
`,
33+
styles: ['react-renderer'],
34+
changeDetection: ChangeDetectionStrategy.OnPush,
35+
host: { 'class': 'fab-search-box' }
36+
})
37+
export class FabSearchBoxComponent extends ReactWrapperComponent<ISearchBoxProps> {
38+
39+
@ViewChild('reactNode') protected reactNodeRef: ElementRef;
40+
41+
@Input() componentRef?: ISearchBoxProps['componentRef'];
42+
@Input() placeholder?: ISearchBoxProps['placeholder'];
43+
@Input() labelText?: ISearchBoxProps['labelText'];
44+
@Input() value?: ISearchBoxProps['value'];
45+
@Input() defaultValue?: ISearchBoxProps['defaultValue'];
46+
@Input() className?: ISearchBoxProps['className'];
47+
@Input() ariaLabel?: ISearchBoxProps['ariaLabel'];
48+
@Input() underlined?: ISearchBoxProps['underlined'];
49+
@Input() theme?: ISearchBoxProps['theme'];
50+
@Input() styles?: ISearchBoxProps['styles'];
51+
@Input() disableAnimation?: ISearchBoxProps['disableAnimation'];
52+
@Input() set clearButtonOptions(value: IButtonOptions) {
53+
this._clearButtonOptions = value;
54+
55+
if (value) {
56+
this.clearButtonProps = this._transformButtonOptionsToProps(value);
57+
}
58+
}
59+
60+
get clearButtonOptions(): IButtonOptions {
61+
return this._clearButtonOptions;
62+
}
63+
64+
@Output() readonly onChange = new EventEmitter<{ newValue: any }>();
65+
@Output() readonly onSearch = new EventEmitter<{ newValue: any }>();
66+
@Output() readonly onClear = new EventEmitter<{ ev?: any }>();
67+
@Output() readonly onEscape = new EventEmitter<{ ev?: any }>();
68+
@Output() readonly onChanged = new EventEmitter<{ newValue: any }>();
69+
70+
clearButtonProps: IButtonProps;
71+
72+
private _clearButtonOptions: IButtonOptions;
73+
74+
constructor(elementRef: ElementRef) {
75+
super(elementRef);
76+
77+
this.onChangeHandler = this.onChangeHandler.bind(this);
78+
this.onSearchHandler = this.onSearchHandler.bind(this);
79+
this.onClearHandler = this.onClearHandler.bind(this);
80+
this.onEscapeHandler = this.onEscapeHandler.bind(this);
81+
this.onChangedHandler = this.onChangedHandler.bind(this);
82+
}
83+
84+
onChangeHandler(newValue: any) {
85+
this.onChange.emit({
86+
newValue
87+
});
88+
}
89+
onSearchHandler(newValue: any) {
90+
this.onSearch.emit({
91+
newValue
92+
});
93+
}
94+
onClearHandler(ev?: any) {
95+
this.onClear.emit({
96+
ev: ev && ev.nativeElement || ev,
97+
});
98+
}
99+
onEscapeHandler(ev?: any) {
100+
this.onEscape.emit({
101+
ev: ev && ev.nativeElement || ev,
102+
});
103+
}
104+
onChangedHandler(newValue: any) {
105+
this.onChange.emit({
106+
newValue
107+
});
108+
}
109+
110+
private _transformButtonOptionsToProps(options: IButtonOptions): IButtonProps {
111+
const sharedProperties = omit(options,
112+
'renderIcon',
113+
'renderText',
114+
'renderDescription',
115+
'renderAriaDescription',
116+
'renderChildren',
117+
'renderMenuIcon',
118+
'renderMenu'
119+
);
120+
121+
const iconRenderer = this.createInputJsxRenderer(options.renderIcon);
122+
const textRenderer = this.createInputJsxRenderer(options.renderText);
123+
const descriptionRenderer = this.createInputJsxRenderer(options.renderDescription);
124+
const ariaDescriptionRenderer = this.createInputJsxRenderer(options.renderAriaDescription);
125+
const childrenRenderer = this.createInputJsxRenderer(options.renderChildren);
126+
const menuIconRenderer = this.createInputJsxRenderer(options.renderMenuIcon);
127+
const menuRenderer = this.createInputJsxRenderer(options.renderMenu);
128+
129+
return Object.assign(
130+
{},
131+
sharedProperties,
132+
iconRenderer && { onRenderIcon: props => iconRenderer(props) } as Pick<IButtonProps, 'onRenderIcon'>,
133+
textRenderer && { onRenderText: props => textRenderer(props) } as Pick<IButtonProps, 'onRenderText'>,
134+
descriptionRenderer && { onRenderDescription: props => descriptionRenderer(props) } as Pick<IButtonProps, 'onRenderDescription'>,
135+
ariaDescriptionRenderer && { onRenderAriaDescription: props => ariaDescriptionRenderer(props) } as Pick<IButtonProps, 'onRenderAriaDescription'>,
136+
childrenRenderer && { onRenderChildren: props => childrenRenderer(props) } as Pick<IButtonProps, 'onRenderChildren'>,
137+
menuIconRenderer && { onRenderMenuIcon: props => menuIconRenderer(props) } as Pick<IButtonProps, 'onRenderMenuIcon'>,
138+
menuRenderer && { onRenderMenu: props => menuRenderer(props) } as Pick<IButtonProps, 'onRenderMenu'>,
139+
);
140+
}
141+
142+
}
143+
144+
export interface IButtonOptions extends Pick<IButtonProps, 'componentRef' | 'href' | 'primary' | 'uniqueId' | 'disabled' | 'allowDisabledFocus' | 'primaryDisabled' | 'styles' | 'theme' | 'checked' | 'className' | 'ariaLabel' | 'ariaDescription' | 'ariaHidden' | 'text' | 'iconProps' | 'menuProps' | 'onAfterMenuDismiss' | 'split' | 'menuIconProps' | 'splitButtonAriaLabel' | 'onMenuClick' | 'secondaryText' | 'toggled' | 'data' | 'getClassNames' | 'getSplitButtonClassNames' | 'menuTriggerKeyCode' | 'keytipProps' | 'persistMenu'> {
145+
renderIcon: InputRendererOptions<IButtonProps>;
146+
renderText: InputRendererOptions<IButtonProps>;
147+
renderDescription: InputRendererOptions<IButtonProps>;
148+
renderAriaDescription: InputRendererOptions<IButtonProps>;
149+
renderChildren: InputRendererOptions<IButtonProps>;
150+
renderMenuIcon: InputRendererOptions<IButtonProps>;
151+
renderMenu: InputRendererOptions<IContextualMenuProps>;
152+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { registerElement } from '@angular-react/core';
2+
import { CommonModule } from '@angular/common';
3+
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
4+
import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox';
5+
import { FabSearchBoxComponent } from './search-box.component';
6+
7+
const components = [
8+
FabSearchBoxComponent,
9+
];
10+
11+
@NgModule({
12+
imports: [CommonModule],
13+
declarations: components,
14+
exports: components,
15+
schemas: [NO_ERRORS_SCHEMA]
16+
})
17+
export class FabSearchBoxModule {
18+
19+
constructor() {
20+
// Add any React elements to the registry (used by the renderer).
21+
registerElement('SearchBox', () => SearchBox);
22+
}
23+
24+
}

0 commit comments

Comments
 (0)