[EC-623] Reusable Search Input Component (#4082)
* [EC-623] Introduce shared organization module and search input component * [EC-623] Add search input story * [EC-623] Rename search input component tag prefix from bit->app
This commit is contained in:
parent
750ff736cd
commit
c002573581
|
@ -1,11 +1,10 @@
|
||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
import { SharedModule } from "../shared";
|
|
||||||
|
|
||||||
import { AccessSelectorModule } from "./components/access-selector";
|
import { AccessSelectorModule } from "./components/access-selector";
|
||||||
import { OrganizationsRoutingModule } from "./organization-routing.module";
|
import { OrganizationsRoutingModule } from "./organization-routing.module";
|
||||||
|
import { SharedOrganizationModule } from "./shared";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [SharedModule, AccessSelectorModule, OrganizationsRoutingModule],
|
imports: [SharedOrganizationModule, AccessSelectorModule, OrganizationsRoutingModule],
|
||||||
})
|
})
|
||||||
export class OrganizationModule {}
|
export class OrganizationModule {}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<label class="tw-sr-only" [for]="id">{{ "search" | i18n }}</label>
|
||||||
|
<div class="tw-relative tw-flex tw-items-center">
|
||||||
|
<label
|
||||||
|
[for]="id"
|
||||||
|
aria-hidden="true"
|
||||||
|
class="tw-absolute tw-left-2 tw-z-20 !tw-mb-0 tw-cursor-text"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-search bwi-fw tw-text-muted"></i>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
bitInput
|
||||||
|
type="search"
|
||||||
|
[id]="id"
|
||||||
|
[placeholder]="placeholder ?? ('search' | i18n)"
|
||||||
|
class="tw-rounded-l tw-pl-9"
|
||||||
|
[ngModel]="searchText"
|
||||||
|
(ngModelChange)="onChange($event)"
|
||||||
|
(blur)="onTouch()"
|
||||||
|
[disabled]="disabled"
|
||||||
|
/>
|
||||||
|
</div>
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { Component, Input } from "@angular/core";
|
||||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||||
|
|
||||||
|
let nextId = 0;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-search-input",
|
||||||
|
templateUrl: "./search-input.component.html",
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: SearchInputComponent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class SearchInputComponent implements ControlValueAccessor {
|
||||||
|
private notifyOnChange: (v: string) => void;
|
||||||
|
private notifyOnTouch: () => void;
|
||||||
|
|
||||||
|
protected id = `search-id-${nextId++}`;
|
||||||
|
protected searchText: string;
|
||||||
|
|
||||||
|
@Input() disabled: boolean;
|
||||||
|
@Input() placeholder: string;
|
||||||
|
|
||||||
|
onChange(searchText: string) {
|
||||||
|
if (this.notifyOnChange != undefined) {
|
||||||
|
this.notifyOnChange(searchText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouch() {
|
||||||
|
if (this.notifyOnTouch != undefined) {
|
||||||
|
this.notifyOnTouch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: (v: string) => void): void {
|
||||||
|
this.notifyOnChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: () => void): void {
|
||||||
|
this.notifyOnTouch = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(searchText: string): void {
|
||||||
|
this.searchText = searchText;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean) {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
import { Meta, moduleMetadata, Story } from "@storybook/angular";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { InputModule } from "@bitwarden/components/src/input/input.module";
|
||||||
|
|
||||||
|
import { PreloadedEnglishI18nModule } from "../../../../tests/preloaded-english-i18n.module";
|
||||||
|
|
||||||
|
import { SearchInputComponent } from "./search-input.component";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Web/Organizations/Search Input",
|
||||||
|
component: SearchInputComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [
|
||||||
|
InputModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
PreloadedEnglishI18nModule,
|
||||||
|
JslibModule,
|
||||||
|
],
|
||||||
|
providers: [],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story<SearchInputComponent> = (args: SearchInputComponent) => ({
|
||||||
|
props: args,
|
||||||
|
template: `
|
||||||
|
<app-search-input [(ngModel)]="searchText" [placeholder]="placeholder" [disabled]="disabled"></app-search-input>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {};
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./shared-organization.module";
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
|
import { SharedModule } from "../../shared";
|
||||||
|
|
||||||
|
import { SearchInputComponent } from "./components/search-input/search-input.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [SharedModule],
|
||||||
|
declarations: [SearchInputComponent],
|
||||||
|
exports: [SharedModule, SearchInputComponent],
|
||||||
|
})
|
||||||
|
export class SharedOrganizationModule {}
|
Loading…
Reference in New Issue