From 7cb49446977dd830325a4a2ebe57f4aba6764b03 Mon Sep 17 00:00:00 2001 From: csehatt741 Date: Mon, 2 Jun 2025 09:45:41 +0200 Subject: [PATCH] Code review cahnges --- CHANGELOG.md | 1 + libs/ui/src/lib/mocks/httpClient.mock.ts | 18 +++++ .../mocks/httpClient.mock.ts | 23 ------ .../symbol-autocomplete.component.stories.ts | 78 +++++++++---------- .../symbol-autocomplete.component.ts | 31 +++++--- 5 files changed, 77 insertions(+), 74 deletions(-) create mode 100644 libs/ui/src/lib/mocks/httpClient.mock.ts delete mode 100644 libs/ui/src/lib/symbol-autocomplete/mocks/httpClient.mock.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbfdad28..93ff0fd3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Extended the symbol search component by default options - Upgraded `ng-extract-i18n-merge` from version `2.15.0` to `2.15.1` - Upgraded `Nx` from version `20.8.1` to `21.1.2` diff --git a/libs/ui/src/lib/mocks/httpClient.mock.ts b/libs/ui/src/lib/mocks/httpClient.mock.ts new file mode 100644 index 000000000..6ef79af61 --- /dev/null +++ b/libs/ui/src/lib/mocks/httpClient.mock.ts @@ -0,0 +1,18 @@ +import { Observable } from 'rxjs'; + +export class HttpClientMock { + public constructor(private mockResponses: Map) {} + + public get(url: string, options?: any): Observable { + if (this.mockResponses.has(url) && options) { + return new Observable((subscriber) => { + subscriber.next(this.mockResponses.get(url)); + subscriber.complete(); + }); + } + + return new Observable((subscriber) => { + subscriber.error(new Error(`No mock data for URL: ${url}`)); + }); + } +} diff --git a/libs/ui/src/lib/symbol-autocomplete/mocks/httpClient.mock.ts b/libs/ui/src/lib/symbol-autocomplete/mocks/httpClient.mock.ts deleted file mode 100644 index 4911760d3..000000000 --- a/libs/ui/src/lib/symbol-autocomplete/mocks/httpClient.mock.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { LookupResponse } from '@ghostfolio/common/interfaces'; - -import { Observable } from 'rxjs'; - -export class HttpClientMock { - public constructor( - private readonly url: string, - private readonly mockData: LookupResponse - ) {} - - get(url: string, options?: any): Observable { - if (url === this.url && options) { - return new Observable((subscriber) => { - subscriber.next(this.mockData as T); - subscriber.complete(); - }); - } - - return new Observable((subscriber) => { - subscriber.error(new Error(`No mock data for URL: ${url}`)); - }); - } -} diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.stories.ts b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.stories.ts index ff1eb6a5c..19b6a616b 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.stories.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.stories.ts @@ -12,9 +12,29 @@ import { import { provideNoopAnimations } from '@angular/platform-browser/animations'; import { applicationConfig, Meta, StoryObj } from '@storybook/angular'; -import { HttpClientMock } from './mocks/httpClient.mock'; +import { HttpClientMock } from '../mocks/httpClient.mock'; import { GfSymbolAutocompleteComponent } from './symbol-autocomplete.component'; +const DEFAULT_OPTIONS: LookupItem[] = [ + { + assetClass: 'COMMODITY', + assetSubClass: 'ETF', + currency: 'USD', + dataProviderInfo: { name: 'YAHOO', isPremium: false }, + dataSource: null, + name: 'Default1', + symbol: 'DEFAULT1' + }, + { + assetClass: 'EQUITY', + assetSubClass: 'STOCK', + currency: 'USD', + dataProviderInfo: { name: 'YAHOO', isPremium: false }, + dataSource: null, + name: 'Default2', + symbol: 'DEFAULT2' + } +]; const FILTERED_OPTIONS: LookupItem[] = [ { assetClass: 'COMMODITY', @@ -22,8 +42,8 @@ const FILTERED_OPTIONS: LookupItem[] = [ currency: 'USD', dataProviderInfo: { name: 'YAHOO', isPremium: false }, dataSource: null, - name: 'Test3', - symbol: 'TEST3' + name: 'Autocomplete1', + symbol: 'AUTOCOMPLETE1' }, { assetClass: 'EQUITY', @@ -31,8 +51,8 @@ const FILTERED_OPTIONS: LookupItem[] = [ currency: 'USD', dataProviderInfo: { name: 'YAHOO', isPremium: false }, dataSource: null, - name: 'Test4', - symbol: 'TEST4' + name: 'Autocomplete2', + symbol: 'AUTOCOMPLETE2' } ]; @@ -53,25 +73,20 @@ export default { }, { provide: HttpClient, - useValue: new HttpClientMock('/api/v1/symbol/lookup', { - items: FILTERED_OPTIONS - }) + useValue: new HttpClientMock( + new Map([ + [ + '/api/v1/symbol/lookup', + { + items: FILTERED_OPTIONS + } + ] + ]) + ) } ] }) - ], - parameters: { - mockData: [ - { - url: '/api/v1/symbol/lookup', - method: 'GET', - status: 200, - response: { - items: FILTERED_OPTIONS - } - } - ] - } + ] } as Meta; type Story = StoryObj; @@ -84,25 +99,6 @@ export const WithoutDefaults: Story = { export const WithDefaults: Story = { args: { - defaultLookupItems: [ - { - assetClass: 'COMMODITY', - assetSubClass: 'ETF', - currency: 'USD', - dataProviderInfo: { name: 'YAHOO', isPremium: false }, - dataSource: null, - name: 'Test1', - symbol: 'TEST1' - }, - { - assetClass: 'EQUITY', - assetSubClass: 'STOCK', - currency: 'USD', - dataProviderInfo: { name: 'YAHOO', isPremium: false }, - dataSource: null, - name: 'Test2', - symbol: 'TEST2' - } - ] + defaultLookupItems: DEFAULT_OPTIONS } }; diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts index ab3e8425a..052d199b9 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts @@ -76,8 +76,9 @@ export class GfSymbolAutocompleteComponent extends AbstractMatFormField implements OnInit, OnDestroy { - public isLoading = false; @Input() public defaultLookupItems: LookupItem[] = []; + @Input() private includeIndices = false; + @Input() public isLoading = false; @ViewChild(MatInput) private input: MatInput; @ViewChild('symbolAutocomplete') public symbolAutocomplete: MatAutocomplete; @@ -105,14 +106,7 @@ export class GfSymbolAutocompleteComponent } if (this.defaultLookupItems?.length) { - this.lookupItems = this.defaultLookupItems.map((lookupItem) => { - return { - ...lookupItem, - assetSubClassString: translate(lookupItem.assetSubClass) - }; - }); - - this.changeDetectorRef.markForCheck(); + this.showDefaultOptions(); } this.control.valueChanges @@ -126,6 +120,11 @@ export class GfSymbolAutocompleteComponent this.control.valueChanges .pipe( filter((query) => { + if (query.length === 0) { + this.showDefaultOptions(); + return false; + } + return isString(query) && query.length > 1; }), tap(() => { @@ -138,7 +137,8 @@ export class GfSymbolAutocompleteComponent takeUntil(this.unsubscribeSubject), switchMap((query: string) => { return this.dataService.fetchSymbols({ - query + query, + includeIndices: this.includeIndices }); }) ) @@ -201,6 +201,17 @@ export class GfSymbolAutocompleteComponent this.unsubscribeSubject.complete(); } + private showDefaultOptions() { + this.lookupItems = this.defaultLookupItems.map((lookupItem) => { + return { + ...lookupItem, + assetSubClassString: translate(lookupItem.assetSubClass) + }; + }); + + this.changeDetectorRef.markForCheck(); + } + private validateRequired() { const requiredCheck = super.required ? !super.value?.dataSource || !super.value?.symbol