Skip to content

Commit

Permalink
feat: search config slots adjustments (#173)
Browse files Browse the repository at this point in the history
* feat: adjusted search header and interactive data view usage

* fix: lint fix

* fix: only visible form values added to search

* feat: working except dates with times

* feat: process config data in reducers

* feat: search configuration update changes

* fix: fixed  webpack module expose

* fix: removed unwanted html

* fix: removed unwanted parameter

* feat: passing lg current layout to search header

* feat: permissions for search configs added

* feat: updated permission and remove inputs from search header

* feat: reducer decides what happens on search config changed action

* fix: slot service token imported from libs

* feat: use type from libs for change listening

* feat: reset form group on viewModel update

* feat: review changes

* fix: search criteria update fix

* feat: libs upgrade

* fix: fix and comment out tests

* fix: fix effects tests

* fix: tests for correct data mocks

* fix: remove fit from test
  • Loading branch information
markuczy authored Oct 9, 2024
1 parent dd601b6 commit 749450b
Show file tree
Hide file tree
Showing 15 changed files with 3,340 additions and 5,099 deletions.
1 change: 1 addition & 0 deletions helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ app:
DELETE: 'Delete tenant'
VIEW: 'View tenant details'
SEARCH: 'Search tenants'
USE_SEARCHCONFIGS: 'Use search configs in tenant'
8,099 changes: 3,114 additions & 4,985 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@
"@ngrx/store-devtools": "^18.0.1",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@onecx/accelerator": "^5.13.0",
"@onecx/angular-accelerator": "^5.13.0",
"@onecx/angular-auth": "^5.13.0",
"@onecx/angular-integration-interface": "^5.13.0",
"@onecx/angular-webcomponents": "^5.13.0",
"@onecx/integration-interface": "^5.13.0",
"@onecx/keycloak-auth": "^5.13.0",
"@onecx/ngrx-accelerator": "^5.13.0",
"@onecx/accelerator": "^5.16.1",
"@onecx/angular-accelerator": "^5.16.1",
"@onecx/angular-auth": "^5.16.1",
"@onecx/angular-integration-interface": "^5.16.1",
"@onecx/angular-webcomponents": "^5.16.1",
"@onecx/integration-interface": "^5.16.1",
"@onecx/keycloak-auth": "^5.16.1",
"@onecx/ngrx-accelerator": "^5.16.1",
"@onecx/nx-plugin": "1.8.1",
"@onecx/portal-integration-angular": "^5.13.0",
"@onecx/portal-layout-styles": "^5.13.0",
"@onecx/portal-integration-angular": "^5.16.1",
"@onecx/portal-layout-styles": "^5.16.1",
"@webcomponents/webcomponentsjs": "^2.8.0",
"fast-deep-equal": "^3.1.3",
"keycloak-angular": "^16.0.1",
Expand Down
6 changes: 4 additions & 2 deletions src/app/tenant/pages/tenant-search/tenant-search.actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createActionGroup, emptyProps, props } from '@ngrx/store'
import { DataTableColumn } from '@onecx/angular-accelerator'
import { DataTableColumn, SearchConfigData } from '@onecx/angular-accelerator'
import { Tenant } from '../../../shared/generated'
import { TenantSearchCriteria } from './tenant-search.parameters'

Expand All @@ -10,7 +10,9 @@ export const TenantSearchActions = createActionGroup({
searchCriteria: TenantSearchCriteria
}>(),
'Reset button clicked': emptyProps(),

'Search config selected': props<{
searchConfig: SearchConfigData | undefined
}>(),
'tenant search results received': props<{
results: Tenant[]
totalElements: number
Expand Down
10 changes: 8 additions & 2 deletions src/app/tenant/pages/tenant-search/tenant-search.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
(resetted)="resetSearch()"
[manualBreadcrumbs]="false"
[actions]="(headerActions$ | async) ?? []"
searchConfigPermission="TENANT#USE_SEARCHCONFIGS"
[viewMode]="vm.viewMode"
pageName="PAGE_TENANT_SEARCH"
(viewModeChanged)="viewModeChanged($event)"
(selectedSearchConfigChanged)="searchConfigInfoSelectionChanged($event)"
>
<form [formGroup]="tenantSearchFormGroup">
<div class="grid mt-0 p-fluid">
Expand All @@ -17,9 +21,10 @@
id="tenant_search_orgId"
type="text"
class="w-18rem"
[title]="'TENANT_SEARCH.TOOLTIPS.ORGID' | translate"
formControlName="orgId"
[(ngModel)]="vm.searchCriteria.orgId"
[pTooltip]="'TENANT_SEARCH.TOOLTIPS.ORGID' | translate"
tooltipPosition="top"
tooltipEvent="hover"
/>
<label for="tenant_search_orgId" style="white-space: nowrap">
{{ 'TENANT_SEARCH.CRITERIA.ORGID' | translate }}</label
Expand All @@ -35,6 +40,7 @@
[data]="vm.results"
[columns]="vm.columns"
viewPermission="TENANT#VIEW"
searchConfigPermission="TENANT#USE_SEARCHCONFIGS"
[listGridPaginator]="false"
[emptyResultsMessage]="'TENANT_SEARCH.EMPTY_RESULTS' | translate"
[supportedViewLayouts]="['table']"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ describe('TenantSearchComponent', () => {
changeMe: '123'
})
component.tenantSearchFormGroup = formValue
component.visibleFormControls = [
{
name: 'changeMe'
}
] as any

store.scannedActions$.pipe(ofType(TenantSearchActions.searchButtonClicked)).subscribe((a) => {
expect(a.searchCriteria).toEqual({ changeMe: '123' })
Expand Down
61 changes: 45 additions & 16 deletions src/app/tenant/pages/tenant-search/tenant-search.component.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { Component, Inject, LOCALE_ID, OnInit } from '@angular/core'
import { FormBuilder, FormGroup } from '@angular/forms'
import { Component, Inject, LOCALE_ID, OnInit, QueryList, ViewChildren } from '@angular/core'
import { FormBuilder, FormControlName, FormGroup } from '@angular/forms'
import { Store } from '@ngrx/store'
import {
Action,
BreadcrumbService,
DataTableColumn,
ExportDataService,
PortalDialogService
PortalDialogService,
SearchConfigData
} from '@onecx/portal-integration-angular'
import { PrimeIcons } from 'primeng/api'
import { first, map, Observable } from 'rxjs'
import { distinctUntilChanged, first, map, Observable } from 'rxjs'
import { isValidDate } from '../../../shared/utils/isValidDate.utils'
import { TenantSearchActions } from './tenant-search.actions'
import { TenantSearchCriteria, tenantSearchCriteriasSchema } from './tenant-search.parameters'
import { selectTenantSearchViewModel } from './tenant-search.selectors'
import { TenantSearchViewModel } from './tenant-search.viewmodel'
import * as deepEqual from 'fast-deep-equal'

@Component({
selector: 'app-tenant-search',
Expand Down Expand Up @@ -62,6 +64,8 @@ export class TenantSearchComponent implements OnInit {
>)
} satisfies Record<keyof TenantSearchCriteria, unknown>)

@ViewChildren(FormControlName) visibleFormControls!: QueryList<FormControlName>

constructor(
private readonly breadcrumbService: BreadcrumbService,
private readonly store: Store,
Expand All @@ -79,27 +83,46 @@ export class TenantSearchComponent implements OnInit {
routerLink: '/tenant'
}
])

this.viewModel$
.pipe(
map((vm) => vm.searchCriteria),
distinctUntilChanged(deepEqual)
)
.subscribe((sc) => {
this.tenantSearchFormGroup.reset(sc)
})
}

searchConfigInfoSelectionChanged(searchConfig: SearchConfigData | undefined) {
this.store.dispatch(
TenantSearchActions.searchConfigSelected({
searchConfig: searchConfig
})
)
}

search(formValue: FormGroup) {
const searchCriteria = Object.entries(formValue.getRawValue()).reduce(
(acc: Partial<TenantSearchCriteria>, [key, value]) => ({
...acc,
[key]: isValidDate(value)
? new Date(
Date.UTC(
value.getFullYear(),
value.getMonth(),
value.getDay(),
value.getHours(),
value.getMinutes(),
value.getSeconds()
[key]: this.isVisible(key)
? isValidDate(value)
? new Date(
Date.UTC(
value.getFullYear(),
value.getMonth(),
value.getDate(),
value.getHours(),
value.getMinutes(),
value.getSeconds()
)
)
).toISOString()
: value || undefined
: value || null
: null
}),
{}
)
) as TenantSearchCriteria
this.store.dispatch(TenantSearchActions.searchButtonClicked({ searchCriteria }))
}

Expand Down Expand Up @@ -128,4 +151,10 @@ export class TenantSearchComponent implements OnInit {
toggleChartVisibility() {
this.store.dispatch(TenantSearchActions.chartVisibilityToggled())
}

private isVisible(control: string) {
return this.visibleFormControls.some(
(formControl) => formControl.name !== null && String(formControl.name) === control
)
}
}
119 changes: 77 additions & 42 deletions src/app/tenant/pages/tenant-search/tenant-search.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { PortalMessageService } from '@onecx/portal-integration-angular'
import { TenantBffService } from 'src/app/shared/generated'
import { TenantSearchEffects } from './tenant-search.effects'
import { TenantSearchActions } from './tenant-search.actions'
import { selectSearchCriteria, tenantSearchSelectors } from './tenant-search.selectors'
import { tenantSearchSelectors } from './tenant-search.selectors'
import { TenantSearchComponent } from './tenant-search.component'

class MockRouter implements Partial<Router> {
Expand Down Expand Up @@ -227,25 +227,30 @@ describe('TenantSearchEffects:', () => {
}
jest.spyOn(mockedTenantService, 'searchTenants').mockReturnValue(of(tenants) as any)

const previousSearchCriteria = {
const previousSearchCriteriaParams = {
orgId: 'prev_org_id',
pageNumber: 1,
pageSize: 1
pageNumber: '1',
pageSize: '1'
}
const newSearchCriteriaParams = {
orgId: 'org_id',
pageNumber: '1',
pageSize: '1'
}
const newSearchCriteria = {
orgId: 'org_id',
pageNumber: 1,
pageSize: 1
}
store.overrideSelector(selectSearchCriteria, newSearchCriteria)
store.overrideSelector(tenantSearchSelectors.selectCriteria, newSearchCriteria)

const effects = initEffects()

const routerNavigatedAction = {
type: ROUTER_NAVIGATED
} as RouterNavigatedAction
mockedRouter.routeFor(TenantSearchComponent)
mockedRouter.configureQueryParams(routerNavigatedAction, previousSearchCriteria, newSearchCriteria)
mockedRouter.configureQueryParams(routerNavigatedAction, previousSearchCriteriaParams, newSearchCriteriaParams)
mockedRouter.simulateNavigation(routerNavigatedAction)

effects.searchByUrl$.subscribe((action) => {
Expand All @@ -265,25 +270,30 @@ describe('TenantSearchEffects:', () => {
}
jest.spyOn(mockedTenantService, 'searchTenants').mockReturnValue(throwError(() => error))

const previousSearchCriteria = {
const previousSearchCriteriaParams = {
orgId: 'prev_org_id',
pageNumber: 1,
pageSize: 1
pageNumber: '1',
pageSize: '1'
}
const newSearchCriteriaParams = {
orgId: 'org_id',
pageNumber: '1',
pageSize: '1'
}
const newSearchCriteria = {
orgId: 'org_id',
pageNumber: 1,
pageSize: 1
}
store.overrideSelector(selectSearchCriteria, newSearchCriteria)
store.overrideSelector(tenantSearchSelectors.selectCriteria, newSearchCriteria)

const effects = initEffects()

const routerNavigatedAction = {
type: ROUTER_NAVIGATED
} as RouterNavigatedAction
mockedRouter.routeFor(TenantSearchComponent)
mockedRouter.configureQueryParams(routerNavigatedAction, previousSearchCriteria, newSearchCriteria)
mockedRouter.configureQueryParams(routerNavigatedAction, previousSearchCriteriaParams, newSearchCriteriaParams)
mockedRouter.simulateNavigation(routerNavigatedAction)

effects.searchByUrl$.subscribe((action) => {
Expand Down Expand Up @@ -321,54 +331,79 @@ describe('TenantSearchEffects:', () => {
expect(effects.searchByUrl$).toBeObservable(expected)
})

it('should dispatch TenantSearchActions.tenantSearchResultsReceived with the results on TenantSearchActions.searchButtonClicked dispatch', (done) => {
const tenants = {
stream: [
{
id: '1'
},
{
id: '2'
}
],
totalElements: 2
}
jest.spyOn(mockedTenantService, 'searchTenants').mockReturnValue(of(tenants) as any)
it('should navigate when query params are different than search criteria on TenantSearchActions.searchButtonClicked dispatch', (done) => {
const spy = jest.spyOn(mockedRouter, 'navigate')

const effects = initEffects()
mockedRouter.routeFor(TenantSearchComponent)
mockedRouter.setRouterParams({})
;(activatedRouteMock.queryParams as ReplaySubject<any>).next({})
;(activatedRouteMock.queryParams as ReplaySubject<any>).next({
orgId: 'orgId'
})
store.overrideSelector(tenantSearchSelectors.selectCriteria, { orgId: 'differentId' })
effectsActions.next(
TenantSearchActions.searchButtonClicked({
searchCriteria: { orgId: '' }
})
)

effects.searchButtonClicked$.subscribe(() => ({}))

effects.searchByUrl$.subscribe((action) => {
expect(action).toEqual({
type: TenantSearchActions.tenantSearchResultsReceived.type,
results: tenants.stream,
totalElements: tenants.totalElements
})
effects.syncParamsToUrl$.subscribe(() => {
expect(spy).toHaveBeenCalledTimes(1)
done()
})
})

it('should not dispatch any searchByUrl related action on TenantSearchActions.resetButtonClicked dispatch', () => {
it('should not navigate when query params are same as search criteria on TenantSearchActions.searchButtonClicked dispatch', (done) => {
const spy = jest.spyOn(mockedRouter, 'navigate')

const criteria = {
orgId: 'orgId'
}

const effects = initEffects()
mockedRouter.routeFor(TenantSearchComponent)
;(activatedRouteMock.queryParams as ReplaySubject<any>).next({})
;(activatedRouteMock.queryParams as ReplaySubject<any>).next(criteria)
store.overrideSelector(tenantSearchSelectors.selectCriteria, criteria)
effectsActions.next(
hot('-a', {
a: TenantSearchActions.resetButtonClicked()
TenantSearchActions.searchButtonClicked({
searchCriteria: { orgId: '' }
})
)

effects.resetButtonClicked$.subscribe(() => ({}))
effects.syncParamsToUrl$.subscribe(() => {
expect(spy).toHaveBeenCalledTimes(0)
done()
})
})

it('should navigate when query params are different than search criteria on TenantSearchActions.resetButtonClicked dispatch', (done) => {
const spy = jest.spyOn(mockedRouter, 'navigate')

expect(effects.searchByUrl$).toBeObservable(hot('--'))
const effects = initEffects()
;(activatedRouteMock.queryParams as ReplaySubject<any>).next({
orgId: 'orgId'
})
store.overrideSelector(tenantSearchSelectors.selectCriteria, { orgId: 'differentId' })
effectsActions.next(TenantSearchActions.resetButtonClicked())

effects.syncParamsToUrl$.subscribe(() => {
expect(spy).toHaveBeenCalledTimes(1)
done()
})
})

it('should not navigate when query params are same as search criteria on TenantSearchActions.resetButtonClicked dispatch', (done) => {
const spy = jest.spyOn(mockedRouter, 'navigate')

const criteria = {
orgId: 'orgId'
}

const effects = initEffects()
;(activatedRouteMock.queryParams as ReplaySubject<any>).next(criteria)
store.overrideSelector(tenantSearchSelectors.selectCriteria, criteria)
effectsActions.next(TenantSearchActions.resetButtonClicked())

effects.syncParamsToUrl$.subscribe(() => {
expect(spy).toHaveBeenCalledTimes(0)
done()
})
})
})
Loading

0 comments on commit 749450b

Please sign in to comment.