From 4b5bb84124661b46b20aeca02400dbb08d6791de Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:17:37 -0400 Subject: [PATCH] Pre demo rush - This breaks stories --- .../components/vault-filter.component.html | 1 - .../individual-vault/vault.component.html | 9 +- .../vault/individual-vault/vault.component.ts | 26 +- .../src/services/jslib-services.module.ts | 13 +- .../search-filter-builder.stories.ts | 92 ----- .../vault-filter-metadata.service.ts | 91 ++-- .../vault/search/search-history.service.ts | 4 +- .../src/search}/filter-builder.component.ts | 390 +++++++++++------- .../src/search}/filter-builder.stories.ts | 15 +- .../src/search/search.component.html | 4 +- .../components/src/search/search.component.ts | 6 +- libs/components/src/search/search.module.ts | 5 +- 12 files changed, 323 insertions(+), 333 deletions(-) delete mode 100644 libs/angular/src/vault/components/search-filter-builder.stories.ts rename libs/{angular/src/vault/components => components/src/search}/filter-builder.component.ts (52%) rename libs/{angular/src/vault/components => components/src/search}/filter-builder.stories.ts (91%) diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.html b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.html index de2eda3813a..71127e37ee3 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.html +++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.html @@ -4,7 +4,6 @@ placeholder="{{ searchPlaceholder | i18n }}" [(ngModel)]="searchText" (ngModelChange)="onSearchTextChanged($event)" - [history]="searchHistory.history$ | async" [savedFilters]="savedFilters$ | async" (filterSaved)="onFilterSaved($event)" (filterDeleted)="onFilterDeleted($event)" diff --git a/apps/web/src/app/vault/individual-vault/vault.component.html b/apps/web/src/app/vault/individual-vault/vault.component.html index 2a1e2c50348..8b44e1627e4 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.html +++ b/apps/web/src/app/vault/individual-vault/vault.component.html @@ -29,14 +29,7 @@
- +
a?.id)); private vaultItemDialogRef?: DialogRef | undefined; - private organizations$ = this.userId$.pipe( + protected organizations$ = this.userId$.pipe( switchMap((id) => this.organizationService.organizations$(id)), ); @@ -241,6 +248,9 @@ export class VaultComponent implements OnInit, OnDestroy { shareReplay({ refCount: false, bufferSize: 1 }), ); + private _searchContext = new Subject(); + protected searchContext$ = this._searchContext.asObservable(); + constructor( private syncService: SyncService, private route: ActivatedRoute, @@ -359,6 +369,10 @@ export class VaultComponent implements OnInit, OnDestroy { }); }), ); + + // TODO: WHO AM I? + context$.pipe(takeUntil(this.destroy$)).subscribe(this._searchContext); + const savedFilters$ = this.accountService.activeAccount$.pipe( switchMap((account) => this.savedFilterService.filtersFor$(account.id)), map((filters) => { @@ -648,8 +662,8 @@ export class VaultComponent implements OnInit, OnDestroy { } }; - filterSearchText(searchText: string) { - this.searchText$.next(searchText); + filterSearchText(searchText: Filter) { + this.searchText$.next(searchText.raw); } /** diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 587f50fb007..aac9eccfa72 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -269,10 +269,8 @@ import { } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service"; import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; -import { - DefaultVaultFilterMetadataService, - VaultFilterMetadataService, -} from "@bitwarden/common/vault/filtering/vault-filter-metadata.service"; +import { BasicVaultFilterHandler } from "@bitwarden/common/vault/filtering/basic-vault-filter.handler"; +import { VaultFilterMetadataService } from "@bitwarden/common/vault/filtering/vault-filter-metadata.service"; import { DefaultFilterService, FilterService } from "@bitwarden/common/vault/search/filter.service"; import { SavedFiltersService, @@ -1500,9 +1498,14 @@ const safeProviders: SafeProvider[] = [ }), safeProvider({ provide: VaultFilterMetadataService, - useClass: DefaultVaultFilterMetadataService, + useClass: VaultFilterMetadataService, deps: [], }), + safeProvider({ + provide: BasicVaultFilterHandler, + useClass: BasicVaultFilterHandler, + deps: [LogService], + }), ]; @NgModule({ diff --git a/libs/angular/src/vault/components/search-filter-builder.stories.ts b/libs/angular/src/vault/components/search-filter-builder.stories.ts deleted file mode 100644 index 4fe9bec8d54..00000000000 --- a/libs/angular/src/vault/components/search-filter-builder.stories.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; -import { map, of } from "rxjs"; - -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { CipherType, FieldType } from "@bitwarden/common/vault/enums"; -import { - CustomFieldMetadata, - VaultFilterMetadata, - VaultFilterMetadataService, -} from "@bitwarden/common/vault/filtering/vault-filter-metadata.service"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { I18nMockService } from "@bitwarden/components"; -// eslint-disable-next-line no-restricted-imports -import { SearchComponent } from "@bitwarden/components/src/search/search.component"; - -import { FilterBuilderComponent } from "./filter-builder.component"; - -export default { - title: "Filter/In Search", - component: SearchComponent, - decorators: [ - moduleMetadata({ - imports: [FilterBuilderComponent], - providers: [ - { - provide: I18nService, - useValue: new I18nMockService({ - search: "Search", - multiSelectLoading: "Loading", - multiSelectNotFound: "Not Found", - multiSelectClearAll: "Clear All", - }), - }, - { - provide: VaultFilterMetadataService, - useValue: { - collectMetadata: () => { - return map((_ciphers) => { - return { - vaults: new Map([ - [null, 1], - ["1", 1], - ["2", 1], - ]), - folders: new Map([ - ["1", 1], - ["2", 1], - ]), - collections: new Map([ - ["1", 1], - ["2", 1], - ]), - itemTypes: new Map([ - [CipherType.Login, 1], - [CipherType.Card, 1], - [CipherType.Identity, 1], - [CipherType.SecureNote, 1], - [CipherType.SshKey, 1], - ]), - customFields: new Map([ - [{ name: "one", type: FieldType.Boolean, linkedType: null }, 1], - [{ name: "one", type: FieldType.Boolean, linkedType: null }, 1], - [{ name: "one", type: FieldType.Boolean, linkedType: null }, 1], - ]), - attachmentCount: 1, - } satisfies VaultFilterMetadata; - }); - }, - } satisfies VaultFilterMetadataService, - }, - ], - }), - ], -} as Meta; - -export const Default: StoryObj = { - render: (args) => ({ - props: args, - template: /*html*/ ` - -
- -
-
-

Other content below

- `, - }), - args: { - ciphers: of([]), - history: ["One", "Two"], - }, -}; diff --git a/libs/common/src/vault/filtering/vault-filter-metadata.service.ts b/libs/common/src/vault/filtering/vault-filter-metadata.service.ts index c071ef998f9..873545cd76a 100644 --- a/libs/common/src/vault/filtering/vault-filter-metadata.service.ts +++ b/libs/common/src/vault/filtering/vault-filter-metadata.service.ts @@ -1,4 +1,4 @@ -import { OperatorFunction, map } from "rxjs"; +import { map } from "rxjs"; import { CipherType, FieldType, LinkedIdType } from "../enums"; import { CipherView } from "../models/view/cipher.view"; @@ -30,11 +30,7 @@ function metaDataKeyEqual(a: T, b: T) { } } -export abstract class VaultFilterMetadataService { - abstract collectMetadata(): OperatorFunction; -} - -export class DefaultVaultFilterMetadataService implements VaultFilterMetadataService { +export class VaultFilterMetadataService { collectMetadata() { const setOrIncrement = (map: Map, key: T) => { const entry = Array.from(map.entries()).find(([k]) => metaDataKeyEqual(key, k)); @@ -47,53 +43,56 @@ export class DefaultVaultFilterMetadataService implements VaultFilterMetadataSer }; return map((ciphers) => { - return ciphers.reduce( - (metadata, cipher) => { - // Track type - setOrIncrement(metadata.itemTypes, cipher.type); + const emptyMetadata = { + vaults: new Map(), + customFields: new Map(), + itemTypes: new Map(), + folders: new Map(), + collections: new Map(), + attachmentCount: 0, + }; - // Track vault - setOrIncrement(metadata.vaults, cipher.organizationId ?? null); + if (ciphers == null) { + return emptyMetadata; + } - // Track all field names - if (cipher.fields != null) { - for (const field of cipher.fields) { - setOrIncrement(metadata.customFields, { - name: field.name, - type: field.type, - linkedType: field.linkedId, - }); - } + return ciphers.reduce((metadata, cipher) => { + // Track type + setOrIncrement(metadata.itemTypes, cipher.type); + + // Track vault + setOrIncrement(metadata.vaults, cipher.organizationId ?? null); + + // Track all field names + if (cipher.fields != null) { + for (const field of cipher.fields) { + setOrIncrement(metadata.customFields, { + name: field.name, + type: field.type, + linkedType: field.linkedId, + }); } + } - // Track all folder ids - if (cipher.folderId != null) { - setOrIncrement(metadata.folders, cipher.folderId); + // Track all folder ids + if (cipher.folderId != null) { + setOrIncrement(metadata.folders, cipher.folderId); + } + + // Track all collections + if (cipher.collectionIds != null) { + for (const collectionId of cipher.collectionIds) { + setOrIncrement(metadata.collections, collectionId); } + } - // Track all collections - if (cipher.collectionIds != null) { - for (const collectionId of cipher.collectionIds) { - setOrIncrement(metadata.collections, collectionId); - } - } + // Track if any have an attachment + if (cipher.attachments != null && cipher.attachments.length > 0) { + metadata.attachmentCount = metadata.attachmentCount + cipher.attachments.length; + } - // Track if any have an attachment - if (cipher.attachments != null && cipher.attachments.length > 0) { - metadata.attachmentCount = metadata.attachmentCount + cipher.attachments.length; - } - - return metadata; - }, - { - vaults: new Map(), - customFields: new Map(), - itemTypes: new Map(), - folders: new Map(), - collections: new Map(), - attachmentCount: 0, - }, - ); + return metadata; + }, emptyMetadata); }); } } diff --git a/libs/common/src/vault/search/search-history.service.ts b/libs/common/src/vault/search/search-history.service.ts index 5060e403d83..815e2a27d92 100644 --- a/libs/common/src/vault/search/search-history.service.ts +++ b/libs/common/src/vault/search/search-history.service.ts @@ -1,4 +1,4 @@ -import { Observable, combineLatestWith, map, mergeMap } from "rxjs"; +import { Observable, combineLatestWith, map, mergeMap, of } from "rxjs"; // eslint-disable-next-line no-restricted-imports -- TODO this will need to move import { KeyService } from "../../../../key-management/src/abstractions/key.service"; @@ -28,7 +28,7 @@ export class SearchHistory { private readonly updateCallback: (newSearch: string) => Promise, ) { this.userHistoryIndexer = orgId ?? userId; - this.history$ = userHistory$.pipe(map((h) => h?.[this.userHistoryIndexer] ?? [])); + this.history$ = of([]); } async push(newSearch: string) { diff --git a/libs/angular/src/vault/components/filter-builder.component.ts b/libs/components/src/search/filter-builder.component.ts similarity index 52% rename from libs/angular/src/vault/components/filter-builder.component.ts rename to libs/components/src/search/filter-builder.component.ts index 5b881bdea64..54598a6d935 100644 --- a/libs/angular/src/vault/components/filter-builder.component.ts +++ b/libs/components/src/search/filter-builder.component.ts @@ -6,31 +6,47 @@ import { Input, OnInit, Output, + signal, } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { BehaviorSubject, distinctUntilChanged, map, Observable, startWith } from "rxjs"; - -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; -import { VaultFilterMetadataService } from "@bitwarden/common/vault/filtering/vault-filter-metadata.service"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { - FormFieldModule, - ButtonModule, - LinkModule, - CheckboxModule, - ChipMultiSelectComponent, - ChipSelectOption, - ChipSelectComponent, - ToggleGroupModule, -} from "@bitwarden/components"; + BehaviorSubject, + debounceTime, + distinctUntilChanged, + firstValueFrom, + map, + Observable, + of, + startWith, + switchMap, +} from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { CipherType } from "@bitwarden/common/vault/enums"; import { BasicFilter, BasicVaultFilterHandler, } from "@bitwarden/common/vault/filtering/basic-vault-filter.handler"; -import { SearchComponent } from "@bitwarden/components/src/search/search.component"; +import { VaultFilterMetadataService } from "@bitwarden/common/vault/filtering/vault-filter-metadata.service"; +import { SearchContext } from "@bitwarden/common/vault/search/query.types"; +import { + FilterName, + FilterString, + SavedFiltersService, +} from "@bitwarden/common/vault/search/saved-filters.service"; + +import { ButtonModule } from "../button"; +import { CheckboxModule } from "../checkbox"; +import { ChipMultiSelectComponent } from "../chip-multi-select"; +import { ChipSelectComponent, ChipSelectOption } from "../chip-select"; +import { FormFieldModule } from "../form-field"; +import { LinkModule } from "../link"; +import { ToggleGroupModule } from "../toggle-group"; + +import { SearchComponent } from "./search.component"; type FilterData = { vaults: ChipSelectOption[] | null; @@ -69,33 +85,39 @@ const customMap = ( }; @Component({ - selector: "app-filter-builder", + selector: "bit-filter-builder", template: `
- +
- @if (mode === "basic") { - - - + @if (mode() === "basic") { + @if (filter.vaults != null && filter.vaults.length > 1) { + + } + @if (filter.folders != null && filter.folders.length > 0) { + + } + @if (filter.collections != null && filter.collections.length > 0) { + + } @for (selectedOtherOption of selectedOptions(); track selectedOtherOption) { @switch (selectedOtherOption) { @case ("types") { @@ -129,9 +151,13 @@ const customMap = ( > - + } + @if (formIsDirty) { + @if (mode() === "basic") { + + } } - + Basic - Advanced + Advanced `, @@ -172,13 +198,13 @@ const customMap = ( export class FilterBuilderComponent implements OnInit { @Input({ required: true }) initialFilter: string; - @Input({ required: true }) ciphers: Observable | undefined; + @Input({ required: true }) searchContext: Observable; @Output() searchFilterEvent = new EventEmitter(); @Output() saveFilterEvent = new EventEmitter(); - protected mode: string = "basic"; + protected mode = signal("basic"); protected form = this.formBuilder.group({ text: this.formBuilder.control(null), @@ -191,6 +217,8 @@ export class FilterBuilderComponent implements OnInit { selectedOtherOptions: this.formBuilder.control([]), }); + protected savedFilters$: Observable>; + private loadingFilter: FilterData; protected filterData$: Observable; @@ -207,6 +235,9 @@ export class FilterBuilderComponent implements OnInit { private readonly formBuilder: FormBuilder, private readonly vaultFilterMetadataService: VaultFilterMetadataService, private readonly basicVaultFilterHandler: BasicVaultFilterHandler, + private readonly logService: LogService, + private readonly savedFilterService: SavedFiltersService, + private readonly accountService: AccountService, ) { // TODO: i18n this.loadingFilter = { @@ -262,9 +293,13 @@ export class FilterBuilderComponent implements OnInit { this.form.controls.otherOptions.setValue(null); }); + this.savedFilters$ = this.accountService.activeAccount$.pipe( + switchMap((acc) => this.savedFilterService.filtersFor$(acc.id)), + ); + this.form.valueChanges .pipe( - // TODO: Debounce? + debounceTime(200), map((v) => this.convertFilter(v)), distinctUntilChanged((previous, current) => { return previous.raw === current.raw; @@ -275,8 +310,7 @@ export class FilterBuilderComponent implements OnInit { } private convertFilter(filter: Partial): Filter { - // TODO: Support advanced mode - if (this.mode === "advanced") { + if (this.mode() === "advanced") { return { type: "advanced", raw: filter.text }; } @@ -298,83 +332,91 @@ export class FilterBuilderComponent implements OnInit { ngOnInit(): void { if (this.initialFilter != null) { - // + if (!this.trySetBasicFilterElements(this.initialFilter)) { + this.form.controls.text.setValue(this.initialFilter); + this.mode.set("advanced"); + } } - this.filterData$ = this.ciphers.pipe( - this.vaultFilterMetadataService.collectMetadata(), - map((metadata) => { - // TODO: Combine with other info - return { - vaults: customMap(metadata.vaults, (v, i) => { - if (v == null) { - // Personal vault + this.filterData$ = this.searchContext.pipe( + switchMap((context) => { + return of(context.ciphers).pipe( + this.vaultFilterMetadataService.collectMetadata(), + map((metadata) => { + { return { - value: null, - label: "My Vault", - }; - } else { - // Get organization info - return { - value: v, - label: `Organization ${i}`, - }; + vaults: customMap(metadata.vaults, (v, i) => { + if (v == null) { + // Personal vault + return { + value: null, + label: "My Vault", + }; + } else { + // Get organization info + const org = context.organizations.find((o) => o.id === v); + + return { + value: org.name, + label: org.name, + }; + } + }), + folders: customMap(metadata.folders, (id, i) => { + const folder = context.folders.find((f) => f.id === id); + return { + value: folder.name, + label: folder.name, + } satisfies ChipSelectOption; + }), + collections: customMap(metadata.collections, (id, i) => { + const collection = context.collections.find((c) => c.id === id); + return { + value: collection.name, + label: collection.name, + } satisfies ChipSelectOption; + }), + types: customMap(metadata.itemTypes, (t) => { + switch (t) { + case CipherType.Login: + return { value: "login", label: "Login", icon: "bwi-globe" }; + case CipherType.Card: + return { + value: "card", + label: "Card", + icon: "bwi-credit-card", + }; + case CipherType.Identity: + return { + value: "identity", + label: "Identity", + icon: "bwi-id-card", + }; + case CipherType.SecureNote: + return { + value: "note", + label: "Secure Note", + icon: "bwi-sticky-note", + }; + case CipherType.SshKey: + return { + value: "sshkey", + label: "SSH Key", + icon: "bwi-key", + }; + default: + throw new Error("Unreachable"); + } + }), + fields: customMap( + metadata.customFields, + (f, i) => ({ value: f.name, label: f.name }) satisfies ChipSelectOption, + ), + anyHaveAttachment: metadata.attachmentCount !== 0, + } satisfies FilterData; } }), - folders: customMap( - metadata.folders, - (f, i) => - ({ - value: f, - label: `Folder ${i}`, - }) satisfies ChipSelectOption, - ), - collections: customMap( - metadata.collections, - (c, i) => - ({ - value: c, - label: `Collection ${i}`, - }) satisfies ChipSelectOption, - ), - types: customMap(metadata.itemTypes, (t) => { - switch (t) { - case CipherType.Login: - return { value: "login", label: "Login", icon: "bwi-globe" }; - case CipherType.Card: - return { - value: "card", - label: "Card", - icon: "bwi-credit-card", - }; - case CipherType.Identity: - return { - value: "identity", - label: "Identity", - icon: "bwi-id-card", - }; - case CipherType.SecureNote: - return { - value: "note", - label: "Secure Note", - icon: "bwi-sticky-note", - }; - case CipherType.SshKey: - return { - value: "sshkey", - label: "SSH Key", - icon: "bwi-key", - }; - default: - throw new Error("Unreachable"); - } - }), - fields: customMap( - metadata.customFields, - (f, i) => ({ value: f.name, label: f.name }) satisfies ChipSelectOption, - ), - anyHaveAttachment: metadata.attachmentCount !== 0, - } satisfies FilterData; + ); }), startWith(this.loadingFilter), ); @@ -384,46 +426,57 @@ export class FilterBuilderComponent implements OnInit { return this.form.controls.selectedOtherOptions.value; } + protected get formIsDirty() { + return this.form.dirty; + } + private trySetBasicFilterElements(value: string) { + if (value == null || value === "") { + this.logService.info("Reseting form."); + this.resetFilter(); + return true; + } + try { + this.logService.info("Parsing", value); const parseResult = this.basicVaultFilterHandler.tryParse(value); - if (parseResult.success) { - if (parseResult.filter.terms.length >= 1) { - throw new Error("More than 1 term not actually supported in basic"); - } - - // This item can be displayed with basic, lets do that. - const selectedOtherOptions: string[] = []; - - if (parseResult.filter.types.length !== 0) { - selectedOtherOptions.push("types"); - } - - if (parseResult.filter.fields.length !== 0) { - selectedOtherOptions.push("fields"); - } - - console.log("Parse advanced query", value, parseResult.filter); - - this.form.setValue({ - text: parseResult.filter.terms.length === 1 ? parseResult.filter.terms[0] : null, - vaults: parseResult.filter.vaults, - folders: parseResult.filter.folders, - collections: parseResult.filter.collections, - fields: parseResult.filter.fields, - types: parseResult.filter.types, - otherOptions: null, - selectedOtherOptions: selectedOtherOptions, - }); - return true; - } else { - // set form to advanced mode and disable switching to basic + if (!parseResult.success) { + // Could not parse query return false; } + + if (parseResult.filter.terms.length >= 1) { + throw new Error("More than 1 term not actually supported in basic"); + } + + // This item can be displayed with basic, lets do that. + const selectedOtherOptions: string[] = []; + + if (parseResult.filter.types.length !== 0) { + selectedOtherOptions.push("types"); + } + + if (parseResult.filter.fields.length !== 0) { + selectedOtherOptions.push("fields"); + } + + const term = parseResult.filter.terms.length === 1 ? parseResult.filter.terms[0] : null; + + this.form.setValue({ + text: term === "" ? null : term, + vaults: parseResult.filter.vaults, + folders: parseResult.filter.folders, + collections: parseResult.filter.collections, + fields: parseResult.filter.fields, + types: parseResult.filter.types, + otherOptions: null, + selectedOtherOptions: selectedOtherOptions, + }); + return true; } catch (err) { // How should I show off parse errors - console.log("Error", err); + this.logService.debug("Error while parsing advanced query", err); return false; } } @@ -441,13 +494,29 @@ export class FilterBuilderComponent implements OnInit { }); } - protected saveFilter() { + protected async saveFilter() { const currentFilter = this.convertFilter(this.form.value); - this.saveFilterEvent.emit(currentFilter.raw); + + if (currentFilter.raw == null || currentFilter.raw === "") { + // Skip + return; + } + + const activeUser = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((acc) => acc.id)), + ); + await this.savedFilterService.saveFilter( + activeUser, + currentFilter.raw as FilterName, + currentFilter.raw as FilterString, + ); } protected modeChanged(newMode: string) { - this.mode = newMode; + if (this.mode() === newMode) { + return; + } + if (newMode === "advanced") { // Switching to advanced, place basic contents into text this.form.controls.text.setValue( @@ -455,10 +524,15 @@ export class FilterBuilderComponent implements OnInit { ); } else { if (!this.trySetBasicFilterElements(this.form.controls.text.value)) { - console.log("Could not set filter back to basic, button should have been disabled."); - this.mode = "advanced"; + this.logService.info( + "Could not set filter back to basic, button should have been disabled.", + ); + // This doesn't actually change the UI, we need to actually disable the button but that + // doesn't look available right now. return; } } + + this.mode.set(newMode); } } diff --git a/libs/angular/src/vault/components/filter-builder.stories.ts b/libs/components/src/search/filter-builder.stories.ts similarity index 91% rename from libs/angular/src/vault/components/filter-builder.stories.ts rename to libs/components/src/search/filter-builder.stories.ts index df82939cadd..87603a0c4c1 100644 --- a/libs/angular/src/vault/components/filter-builder.stories.ts +++ b/libs/components/src/search/filter-builder.stories.ts @@ -2,19 +2,20 @@ import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; import { map, of } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { CipherType, FieldType } from "@bitwarden/common/vault/enums"; +import { BasicVaultFilterHandler } from "@bitwarden/common/vault/filtering/basic-vault-filter.handler"; import { CustomFieldMetadata, VaultFilterMetadata, VaultFilterMetadataService, } from "@bitwarden/common/vault/filtering/vault-filter-metadata.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { I18nMockService } from "@bitwarden/components"; + +import { I18nMockService } from "../utils"; import { FilterBuilderComponent } from "./filter-builder.component"; -import { BasicVaultFilterHandler } from "@bitwarden/common/vault/filtering/basic-vault-filter.handler"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; export default { title: "Filter/Filter Builder", @@ -61,8 +62,8 @@ export default { ]), customFields: new Map([ [{ name: "one", type: FieldType.Boolean, linkedType: null }, 1], - [{ name: "one", type: FieldType.Boolean, linkedType: null }, 1], - [{ name: "one", type: FieldType.Boolean, linkedType: null }, 1], + [{ name: "two", type: FieldType.Boolean, linkedType: null }, 1], + [{ name: "three", type: FieldType.Boolean, linkedType: null }, 1], ]), attachmentCount: 1, } satisfies VaultFilterMetadata; @@ -90,7 +91,7 @@ export const Default: Story = { render: (args) => ({ props: args, template: /*html*/ ` - + `, }), args: { diff --git a/libs/components/src/search/search.component.html b/libs/components/src/search/search.component.html index 68b1e5bdf42..2ab53466819 100644 --- a/libs/components/src/search/search.component.html +++ b/libs/components/src/search/search.component.html @@ -38,7 +38,7 @@ [buttonType]="" > - +
diff --git a/libs/components/src/search/search.component.ts b/libs/components/src/search/search.component.ts index db1d8782f7a..9f44f9c3deb 100644 --- a/libs/components/src/search/search.component.ts +++ b/libs/components/src/search/search.component.ts @@ -8,7 +8,7 @@ import { ReactiveFormsModule, FormsModule, } from "@angular/forms"; -import { BehaviorSubject, map } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; import { isBrowserSafariApi } from "@bitwarden/platform"; import { I18nPipe } from "@bitwarden/ui-common"; @@ -87,9 +87,7 @@ export class SearchComponent implements ControlValueAccessor, FocusableElement { } get filteredHistory$() { - // TODO: Not clear if filtering is better or worse - return this.textUpdated$.pipe(map((text) => this.history)); - // return this.textUpdated$.pipe(map((text) => this.history.filter((h) => h.startsWith(text)))); + return of([]); } private _selectedContent = new BehaviorSubject(null); diff --git a/libs/components/src/search/search.module.ts b/libs/components/src/search/search.module.ts index fa7b59be762..7368079cbce 100644 --- a/libs/components/src/search/search.module.ts +++ b/libs/components/src/search/search.module.ts @@ -2,10 +2,11 @@ import { NgModule } from "@angular/core"; import { ButtonModule } from "../button"; +import { FilterBuilderComponent } from "./filter-builder.component"; import { SearchComponent } from "./search.component"; @NgModule({ - imports: [SearchComponent, ButtonModule], - exports: [SearchComponent], + imports: [SearchComponent, ButtonModule, FilterBuilderComponent], + exports: [SearchComponent, FilterBuilderComponent], }) export class SearchModule {}