diff --git a/src/app/features/collections/components/collections-discover/collections-discover.component.html b/src/app/features/collections/components/collections-discover/collections-discover.component.html
index 1e9261f03..d6971525e 100644
--- a/src/app/features/collections/components/collections-discover/collections-discover.component.html
+++ b/src/app/features/collections/components/collections-discover/collections-discover.component.html
@@ -37,7 +37,13 @@
{{ collectionProvider()?
-
+ @if (useShareTroveSearch) {
+ @if (defaultSearchFiltersInitialized()) {
+
+ }
+ } @else {
+
+ }
} @else {
diff --git a/src/app/features/collections/components/collections-discover/collections-discover.component.spec.ts b/src/app/features/collections/components/collections-discover/collections-discover.component.spec.ts
index 09693f727..7be0470cd 100644
--- a/src/app/features/collections/components/collections-discover/collections-discover.component.spec.ts
+++ b/src/app/features/collections/components/collections-discover/collections-discover.component.spec.ts
@@ -1,129 +1,261 @@
+import { Store } from '@ngxs/store';
+
import { MockComponents, MockProvider } from 'ng-mocks';
+import { Mock } from 'vitest';
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
+import { ENVIRONMENT } from '@core/provider/environment.provider';
+import { GlobalSearchComponent } from '@osf/shared/components/global-search/global-search.component';
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component';
import { CustomDialogService } from '@osf/shared/services/custom-dialog.service';
import { ToastService } from '@osf/shared/services/toast.service';
import { CollectionsSelectors } from '@shared/stores/collections';
+import { SetDefaultFilterValue, SetExtraFilters } from '@shared/stores/global-search';
import { MOCK_PROVIDER } from '@testing/mocks/provider.mock';
import { provideOSFCore } from '@testing/osf.testing.provider';
import { CustomDialogServiceMockBuilder } from '@testing/providers/custom-dialog-provider.mock';
import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock';
import { provideMockStore } from '@testing/providers/store-provider.mock';
-import { ToastServiceMock, ToastServiceMockType } from '@testing/providers/toast-provider.mock';
+import { ToastServiceMock } from '@testing/providers/toast-provider.mock';
import { CollectionsQuerySyncService } from '../../services';
import { CollectionsMainContentComponent } from '../collections-main-content/collections-main-content.component';
import { CollectionsDiscoverComponent } from './collections-discover.component';
-describe('CollectionsDiscoverComponent', () => {
- let component: CollectionsDiscoverComponent;
- let fixture: ComponentFixture;
- let toastServiceMock: ToastServiceMockType;
- let mockCustomDialogService: ReturnType;
- let mockRoute: ReturnType;
-
- beforeEach(() => {
- toastServiceMock = ToastServiceMock.simple();
- mockCustomDialogService = CustomDialogServiceMockBuilder.create().build();
- mockRoute = ActivatedRouteMockBuilder.create().withParams({ providerId: 'provider-1' }).build();
-
- TestBed.configureTestingModule({
- imports: [
- CollectionsDiscoverComponent,
- ...MockComponents(SearchInputComponent, CollectionsMainContentComponent, LoadingSpinnerComponent),
- ],
- providers: [
- provideOSFCore(),
- MockProvider(ToastService, toastServiceMock),
- MockProvider(CustomDialogService, mockCustomDialogService),
- MockProvider(ActivatedRoute, mockRoute),
- provideMockStore({
- signals: [
- { selector: CollectionsSelectors.getCollectionProvider, value: MOCK_PROVIDER },
- { selector: CollectionsSelectors.getCollectionDetails, value: null },
- { selector: CollectionsSelectors.getAllSelectedFilters, value: {} },
- { selector: CollectionsSelectors.getSortBy, value: 'date' },
- { selector: CollectionsSelectors.getSearchText, value: '' },
- { selector: CollectionsSelectors.getPageNumber, value: '1' },
- { selector: CollectionsSelectors.getCollectionProviderLoading, value: false },
- ],
- }),
- ],
- }).overrideComponent(CollectionsDiscoverComponent, {
- set: {
- providers: [MockProvider(CollectionsQuerySyncService)],
+const MOCK_COLLECTION_PROVIDER = {
+ ...MOCK_PROVIDER,
+ primaryCollection: { id: 'collection-1', type: 'collections' },
+ requiredMetadataTemplate: null,
+};
+
+const MOCK_COLLECTION_PROVIDER_WITH_TEMPLATE = {
+ ...MOCK_COLLECTION_PROVIDER,
+ requiredMetadataTemplate: {
+ id: 'template-1',
+ type: 'cedar-metadata-templates' as const,
+ attributes: {
+ schema_name: 'Test',
+ cedar_id: 'cedar-1',
+ template: {
+ '@id': 'https://repo.metadatacenter.org/templates/test',
+ '@type': 'https://schema.metadatacenter.org/core/Template',
+ type: 'object',
+ title: 'Test',
+ description: '',
+ $schema: 'http://json-schema.org/draft-04/schema',
+ '@context': {} as never,
+ required: [],
+ properties: {},
+ _ui: {
+ order: ['field1'],
+ propertyLabels: { field1: 'Field One' },
+ propertyDescriptions: {},
+ },
},
- });
+ },
+ },
+};
- fixture = TestBed.createComponent(CollectionsDiscoverComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
+interface SetupOptions {
+ collectionSubmissionWithCedar?: boolean;
+ provider?: typeof MOCK_COLLECTION_PROVIDER | typeof MOCK_COLLECTION_PROVIDER_WITH_TEMPLATE;
+}
- it('should create', () => {
- expect(component).toBeTruthy();
- });
+function setup(options: SetupOptions = {}) {
+ const { collectionSubmissionWithCedar = false, provider = MOCK_COLLECTION_PROVIDER } = options;
+
+ const toastServiceMock = ToastServiceMock.simple();
+ const mockCustomDialogService = CustomDialogServiceMockBuilder.create().build();
+ const mockRoute = ActivatedRouteMockBuilder.create().withParams({ providerId: 'provider-1' }).build();
- it('should initialize with default values', () => {
- expect(component.providerId()).toBe('provider-1');
- expect(component.searchControl.value).toBe('');
+ TestBed.configureTestingModule({
+ imports: [
+ CollectionsDiscoverComponent,
+ ...MockComponents(
+ SearchInputComponent,
+ CollectionsMainContentComponent,
+ GlobalSearchComponent,
+ LoadingSpinnerComponent
+ ),
+ ],
+ providers: [
+ provideOSFCore(),
+ { provide: ENVIRONMENT, useValue: { apiDomainUrl: 'http://localhost:8000', collectionSubmissionWithCedar } },
+ MockProvider(ToastService, toastServiceMock),
+ MockProvider(CustomDialogService, mockCustomDialogService),
+ MockProvider(ActivatedRoute, mockRoute),
+ provideMockStore({
+ signals: [
+ { selector: CollectionsSelectors.getCollectionProvider, value: provider },
+ { selector: CollectionsSelectors.getCollectionDetails, value: null },
+ { selector: CollectionsSelectors.getAllSelectedFilters, value: {} },
+ { selector: CollectionsSelectors.getSortBy, value: 'date' },
+ { selector: CollectionsSelectors.getSearchText, value: '' },
+ { selector: CollectionsSelectors.getPageNumber, value: '1' },
+ { selector: CollectionsSelectors.getCollectionProviderLoading, value: false },
+ ],
+ }),
+ ],
+ }).overrideComponent(CollectionsDiscoverComponent, {
+ set: {
+ providers: [MockProvider(CollectionsQuerySyncService)],
+ },
});
- it('should handle search triggered', () => {
- const searchValue = 'test search';
+ const fixture = TestBed.createComponent(CollectionsDiscoverComponent);
+ const component = fixture.componentInstance;
+ const store = TestBed.inject(Store);
+ fixture.detectChanges();
- component.onSearchTriggered(searchValue);
+ return { fixture, component, store };
+}
- expect(component).toBeTruthy();
- });
+describe('CollectionsDiscoverComponent', () => {
+ describe('legacy mode (collectionSubmissionWithCedar = false)', () => {
+ let component: CollectionsDiscoverComponent;
+ let fixture: ComponentFixture;
- it('should have provider id signal', () => {
- expect(component.providerId()).toBe('provider-1');
- });
+ beforeEach(() => {
+ ({ fixture, component } = setup());
+ });
- it('should have collection provider data', () => {
- expect(component.collectionProvider()).toEqual(MOCK_PROVIDER);
- });
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
- it('should have collection details', () => {
- expect(component.collectionDetails()).toBeNull();
- });
+ it('should set useShareTroveSearch to false', () => {
+ expect(component.useShareTroveSearch).toBe(false);
+ });
- it('should have selected filters', () => {
- expect(component.selectedFilters()).toEqual({});
- });
+ it('should initialize with default values', () => {
+ expect(component.providerId()).toBe('provider-1');
+ expect(component.searchControl.value).toBe('');
+ });
- it('should have sort by value', () => {
- expect(component.sortBy()).toBe('date');
- });
+ it('should have collection provider data', () => {
+ expect(component.collectionProvider()).toEqual(MOCK_COLLECTION_PROVIDER);
+ });
- it('should have search text', () => {
- expect(component.searchText()).toBe('');
- });
+ it('should have collection details as null', () => {
+ expect(component.collectionDetails()).toBeNull();
+ });
- it('should have page number', () => {
- expect(component.pageNumber()).toBe('1');
- });
+ it('should have selected filters', () => {
+ expect(component.selectedFilters()).toEqual({});
+ });
- it('should have loading state', () => {
- expect(component.isProviderLoading()).toBe(false);
- });
+ it('should have sort by value', () => {
+ expect(component.sortBy()).toBe('date');
+ });
+
+ it('should have search text', () => {
+ expect(component.searchText()).toBe('');
+ });
- it('should compute primary collection id', () => {
- expect(component.primaryCollectionId()).toBe(MOCK_PROVIDER.primaryCollection?.id);
+ it('should have page number', () => {
+ expect(component.pageNumber()).toBe('1');
+ });
+
+ it('should have loading state', () => {
+ expect(component.isProviderLoading()).toBe(false);
+ });
+
+ it('should compute primary collection id', () => {
+ expect(component.primaryCollectionId()).toBe('collection-1');
+ });
+
+ it('should handle search control value changes', () => {
+ component.searchControl.setValue('new search value');
+ expect(component.searchControl.value).toBe('new search value');
+ });
+
+ it('should not initialize default search filters', () => {
+ expect(component.defaultSearchFiltersInitialized()).toBe(false);
+ });
+
+ it('should render CollectionsMainContentComponent', () => {
+ const el = fixture.nativeElement as HTMLElement;
+ expect(el.querySelector('osf-collections-main-content')).toBeTruthy();
+ expect(el.querySelector('osf-global-search')).toBeNull();
+ });
+
+ it('should dispatch setSearchValue and setPageNumber on search triggered', () => {
+ const { component: localComponent, store: localStore } = setup();
+ (localStore.dispatch as Mock).mockClear();
+
+ localComponent.onSearchTriggered('my query');
+
+ const calls = (localStore.dispatch as Mock).mock.calls.flat();
+ expect(calls.some((c: unknown) => c instanceof SetDefaultFilterValue)).toBe(false);
+ });
});
- it('should handle search control value changes', () => {
- const searchValue = 'new search value';
+ describe('shtrove mode (collectionSubmissionWithCedar = true)', () => {
+ it('should set useShareTroveSearch to true', () => {
+ const { component } = setup({ collectionSubmissionWithCedar: true });
+ expect(component.useShareTroveSearch).toBe(true);
+ });
+
+ it('should initialize default search filters', () => {
+ const { component } = setup({ collectionSubmissionWithCedar: true });
+ expect(component.defaultSearchFiltersInitialized()).toBe(true);
+ });
+
+ it('should dispatch SetDefaultFilterValue with collection IRI', () => {
+ const { store } = setup({ collectionSubmissionWithCedar: true });
+ const dispatched = (store.dispatch as Mock).mock.calls.flat();
+ const setDefaultFilter = dispatched.find(
+ (c: unknown) => c instanceof SetDefaultFilterValue
+ ) as SetDefaultFilterValue;
+
+ expect(setDefaultFilter).toBeDefined();
+ expect(setDefaultFilter.filterKey).toBe('isContainedBy');
+ expect(setDefaultFilter.value).toBe('http://localhost:8000/v2/collections/collection-1/');
+ });
+
+ it('should not dispatch SetExtraFilters when provider has no requiredMetadataTemplate', () => {
+ const { store } = setup({ collectionSubmissionWithCedar: true });
+ const dispatched = (store.dispatch as Mock).mock.calls.flat();
- component.searchControl.setValue(searchValue);
+ expect(dispatched.some((c: unknown) => c instanceof SetExtraFilters)).toBe(false);
+ });
+
+ it('should dispatch SetExtraFilters when provider has a requiredMetadataTemplate', () => {
+ const { store } = setup({
+ collectionSubmissionWithCedar: true,
+ provider: MOCK_COLLECTION_PROVIDER_WITH_TEMPLATE,
+ });
- expect(component.searchControl.value).toBe(searchValue);
+ const dispatched = (store.dispatch as Mock).mock.calls.flat();
+ const setExtraFilters = dispatched.find((c: unknown) => c instanceof SetExtraFilters) as SetExtraFilters;
+
+ expect(setExtraFilters).toBeDefined();
+ expect(setExtraFilters.filters).toHaveLength(1);
+ expect(setExtraFilters.filters[0].key).toBe('field1');
+ expect(setExtraFilters.filters[0].label).toBe('Field One');
+ });
+
+ it('should render GlobalSearchComponent when filters are initialized', () => {
+ const { fixture } = setup({ collectionSubmissionWithCedar: true });
+ const el = fixture.nativeElement as HTMLElement;
+
+ expect(el.querySelector('osf-global-search')).toBeTruthy();
+ expect(el.querySelector('osf-collections-main-content')).toBeNull();
+ });
+
+ it('should not dispatch any action on onSearchTriggered in shtrove mode', () => {
+ const { component, store } = setup({ collectionSubmissionWithCedar: true });
+ (store.dispatch as Mock).mockClear();
+
+ component.onSearchTriggered('query');
+
+ expect(store.dispatch).not.toHaveBeenCalled();
+ });
});
});
diff --git a/src/app/features/collections/components/collections-discover/collections-discover.component.ts b/src/app/features/collections/components/collections-discover/collections-discover.component.ts
index 0c43f26cb..af6994b7e 100644
--- a/src/app/features/collections/components/collections-discover/collections-discover.component.ts
+++ b/src/app/features/collections/components/collections-discover/collections-discover.component.ts
@@ -21,8 +21,11 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
+import { ENVIRONMENT } from '@core/provider/environment.provider';
+import { GlobalSearchComponent } from '@osf/shared/components/global-search/global-search.component';
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component';
+import { CedarTemplateFilterMapper } from '@osf/shared/mappers/filters/cedar-template-filter.mapper';
import { CollectionsFilters } from '@osf/shared/models/collections/collections-filters.model';
import { BrandService } from '@osf/shared/services/brand.service';
import { CustomDialogService } from '@osf/shared/services/custom-dialog.service';
@@ -37,6 +40,7 @@ import {
SetPageNumber,
SetSearchValue,
} from '@osf/shared/stores/collections';
+import { ResetSearchState, SetDefaultFilterValue, SetExtraFilters } from '@osf/shared/stores/global-search';
import { CollectionsQuerySyncService } from '../../services';
import { CollectionsHelpDialogComponent } from '../collections-help-dialog/collections-help-dialog.component';
@@ -49,6 +53,7 @@ import { CollectionsMainContentComponent } from '../collections-main-content/col
RouterLink,
SearchInputComponent,
CollectionsMainContentComponent,
+ GlobalSearchComponent,
LoadingSpinnerComponent,
TranslatePipe,
],
@@ -66,10 +71,14 @@ export class CollectionsDiscoverComponent {
private brandService = inject(BrandService);
private headerStyleHelper = inject(HeaderStyleService);
private platformId = inject(PLATFORM_ID);
+ private environment = inject(ENVIRONMENT);
private isBrowser = isPlatformBrowser(this.platformId);
searchControl = new FormControl('');
providerId = signal('');
+ defaultSearchFiltersInitialized = signal(false);
+
+ readonly useShareTroveSearch = this.environment.collectionSubmissionWithCedar;
collectionProvider = select(CollectionsSelectors.getCollectionProvider);
collectionDetails = select(CollectionsSelectors.getCollectionDetails);
@@ -89,12 +98,34 @@ export class CollectionsDiscoverComponent {
setPageNumber: SetPageNumber,
clearCollections: ClearCollections,
clearCollectionsSubmissions: ClearCollectionSubmissions,
+ setDefaultFilterValue: SetDefaultFilterValue,
+ setExtraFilters: SetExtraFilters,
+ resetSearchState: ResetSearchState,
});
constructor() {
this.initializeProvider();
- this.setupEffects();
- this.setupSearchBinding();
+ this.setupBrandingEffect();
+
+ if (this.useShareTroveSearch) {
+ this.setupShareTroveSearchEffect();
+ } else {
+ this.setupCollectionDetailsEffect();
+ this.setupUrlSyncEffect();
+ this.setupLegacySearchEffect();
+ this.setupSearchBinding();
+ }
+
+ this.destroyRef.onDestroy(() => {
+ if (this.isBrowser) {
+ this.actions.clearCollections();
+ if (this.useShareTroveSearch) {
+ this.actions.resetSearchState();
+ }
+ this.headerStyleHelper.resetToDefaults();
+ this.brandService.resetBranding();
+ }
+ });
}
openHelpDialog(): void {
@@ -102,8 +133,10 @@ export class CollectionsDiscoverComponent {
}
onSearchTriggered(searchValue: string): void {
- this.actions.setSearchValue(searchValue);
- this.actions.setPageNumber('1');
+ if (!this.useShareTroveSearch) {
+ this.actions.setSearchValue(searchValue);
+ this.actions.setPageNumber('1');
+ }
}
private initializeProvider(): void {
@@ -117,24 +150,49 @@ export class CollectionsDiscoverComponent {
this.actions.getCollectionProvider(id);
}
- private setupEffects(): void {
- this.querySyncService.initializeFromUrl();
-
+ private setupBrandingEffect(): void {
effect(() => {
- const collectionId = this.primaryCollectionId();
- if (collectionId) {
- this.actions.getCollectionDetails(collectionId);
+ const provider = this.collectionProvider();
+
+ if (provider?.brand) {
+ this.brandService.applyBranding(provider.brand);
+ this.headerStyleHelper.applyHeaderStyles(provider.brand.secondaryColor, provider.brand.backgroundColor || '');
}
});
+ }
+ private setupShareTroveSearchEffect(): void {
effect(() => {
const provider = this.collectionProvider();
+ const collectionId = this.primaryCollectionId();
- if (provider && provider.brand) {
- this.brandService.applyBranding(provider.brand);
- this.headerStyleHelper.applyHeaderStyles(provider.brand.secondaryColor, provider.brand.backgroundColor || '');
+ if (!provider || !collectionId || this.defaultSearchFiltersInitialized()) return;
+
+ const collectionIri = `${this.environment.apiDomainUrl}/v2/collections/${collectionId}/`;
+ this.actions.setDefaultFilterValue('isContainedBy', collectionIri);
+
+ if (provider.requiredMetadataTemplate?.attributes?.template) {
+ const extraFilters = CedarTemplateFilterMapper.fromTemplate(
+ provider.requiredMetadataTemplate.attributes.template
+ );
+ this.actions.setExtraFilters(extraFilters);
+ }
+
+ this.defaultSearchFiltersInitialized.set(true);
+ });
+ }
+
+ private setupCollectionDetailsEffect(): void {
+ effect(() => {
+ const collectionId = this.primaryCollectionId();
+ if (collectionId) {
+ this.actions.getCollectionDetails(collectionId);
}
});
+ }
+
+ private setupUrlSyncEffect(): void {
+ this.querySyncService.initializeFromUrl();
effect(() => {
const searchText = this.searchText();
@@ -146,7 +204,9 @@ export class CollectionsDiscoverComponent {
this.querySyncService.syncStoreToUrl(searchText, sortBy, selectedFilters, pageNumber);
}
});
+ }
+ private setupLegacySearchEffect(): void {
effect(() => {
const searchText = this.searchText();
const sortBy = this.sortBy();
@@ -161,19 +221,11 @@ export class CollectionsDiscoverComponent {
this.actions.searchCollectionSubmissions(providerId, searchText, activeFilters, pageNumber, sortBy);
}
});
-
- this.destroyRef.onDestroy(() => {
- if (this.isBrowser) {
- this.actions.clearCollections();
- this.headerStyleHelper.resetToDefaults();
- this.brandService.resetBranding();
- }
- });
}
private getActiveFilters(filters: CollectionsFilters): Record {
return Object.entries(filters)
- .filter(([_, value]) => value.length)
+ .filter(([, value]) => value.length)
.reduce(
(acc, [key, value]) => {
acc[key] = value;
diff --git a/src/app/shared/mappers/filters/cedar-template-filter.mapper.ts b/src/app/shared/mappers/filters/cedar-template-filter.mapper.ts
new file mode 100644
index 000000000..44d32e9d8
--- /dev/null
+++ b/src/app/shared/mappers/filters/cedar-template-filter.mapper.ts
@@ -0,0 +1,16 @@
+import { CedarTemplate } from '@osf/features/metadata/models';
+import { DiscoverableFilter, FilterOperatorOption } from '@osf/shared/models/search/discaverable-filter.model';
+
+export class CedarTemplateFilterMapper {
+ static fromTemplate(template: CedarTemplate): DiscoverableFilter[] {
+ const { order, propertyLabels } = template._ui;
+
+ return order
+ .filter((key) => propertyLabels[key]?.trim())
+ .map((key) => ({
+ key,
+ label: propertyLabels[key],
+ operator: FilterOperatorOption.AnyOf,
+ }));
+ }
+}
diff --git a/src/app/shared/stores/global-search/global-search.actions.ts b/src/app/shared/stores/global-search/global-search.actions.ts
index 00dfa8d38..dcf59ed74 100644
--- a/src/app/shared/stores/global-search/global-search.actions.ts
+++ b/src/app/shared/stores/global-search/global-search.actions.ts
@@ -1,6 +1,6 @@
import { StringOrNull } from '@osf/shared/helpers/types.helper';
import { ResourceType } from '@shared/enums/resource-type.enum';
-import { FilterOption } from '@shared/models/search/discaverable-filter.model';
+import { DiscoverableFilter, FilterOption } from '@shared/models/search/discaverable-filter.model';
export class FetchResources {
static readonly type = '[GlobalSearch] Fetch Resources';
@@ -81,6 +81,12 @@ export class LoadMoreFilterOptions {
constructor(public filterKey: string) {}
}
+export class SetExtraFilters {
+ static readonly type = '[GlobalSearch] Set Extra Filters';
+
+ constructor(public filters: DiscoverableFilter[]) {}
+}
+
export class ResetSearchState {
static readonly type = '[GlobalSearch] Reset Search State';
}
diff --git a/src/app/shared/stores/global-search/global-search.model.ts b/src/app/shared/stores/global-search/global-search.model.ts
index 2174a080c..c32508adf 100644
--- a/src/app/shared/stores/global-search/global-search.model.ts
+++ b/src/app/shared/stores/global-search/global-search.model.ts
@@ -7,6 +7,7 @@ import { AsyncStateModel } from '@osf/shared/models/store/async-state.model';
export interface GlobalSearchStateModel {
resources: AsyncStateModel;
filters: DiscoverableFilter[];
+ extraFilters: DiscoverableFilter[];
defaultFilterOptions: Record;
selectedFilterOptions: Record;
filterOptionsCache: Record;
@@ -28,6 +29,7 @@ export const GLOBAL_SEARCH_STATE_DEFAULTS = {
error: null,
},
filters: [],
+ extraFilters: [],
defaultFilterOptions: {},
selectedFilterOptions: {},
filterOptionsCache: {},
diff --git a/src/app/shared/stores/global-search/global-search.state.ts b/src/app/shared/stores/global-search/global-search.state.ts
index 78e7c552b..f45c946e3 100644
--- a/src/app/shared/stores/global-search/global-search.state.ts
+++ b/src/app/shared/stores/global-search/global-search.state.ts
@@ -20,6 +20,7 @@ import {
LoadMoreFilterOptions,
ResetSearchState,
SetDefaultFilterValue,
+ SetExtraFilters,
SetResourceType,
SetSearchText,
SetSortBy,
@@ -238,6 +239,11 @@ export class GlobalSearchState {
ctx.patchState({ defaultFilterOptions: updatedFilterValues });
}
+ @Action(SetExtraFilters)
+ setExtraFilters(ctx: StateContext, action: SetExtraFilters) {
+ ctx.patchState({ extraFilters: action.filters });
+ }
+
@Action(UpdateSelectedFilterOption)
updateSelectedFilterOption(ctx: StateContext, action: UpdateSelectedFilterOption) {
const updatedFilterValues = { ...ctx.getState().selectedFilterOptions, [action.filterKey]: action.filterOption };
@@ -268,12 +274,16 @@ export class GlobalSearchState {
}
private updateResourcesState(ctx: StateContext, response: ResourcesData) {
+ const { extraFilters } = ctx.getState();
+ const apiFilterKeys = new Set(response.filters.map((f) => f.key));
+ const merged = [...response.filters, ...extraFilters.filter((f) => !apiFilterKeys.has(f.key))];
+
ctx.patchState({
resources: { data: response.resources, isLoading: false, error: null },
filterOptionsCache: {},
filterSearchCache: {},
filterPaginationCache: {},
- filters: response.filters,
+ filters: merged,
resourcesCount: response.count,
first: response.first,
next: response.next,
diff --git a/src/testing/providers/environment.token.mock.ts b/src/testing/providers/environment.token.mock.ts
index 02105aed2..7bc33d525 100644
--- a/src/testing/providers/environment.token.mock.ts
+++ b/src/testing/providers/environment.token.mock.ts
@@ -47,5 +47,6 @@ export const EnvironmentTokenMock = {
newRelicLoaderConfigAgentID: '',
newRelicLoaderConfigLicenseKey: '',
newRelicLoaderConfigApplicationID: '',
+ collectionSubmissionWithCedar: false,
},
};