@cccsaurora/howler-ui 2.18.0-dev.676 → 2.18.0-dev.682
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/index.d.ts +2 -0
- package/api/index.js +4 -2
- package/api/search/case.d.ts +4 -0
- package/api/search/case.js +8 -0
- package/api/search/index.d.ts +2 -1
- package/api/search/index.js +2 -1
- package/api/v2/case/index.d.ts +6 -0
- package/api/v2/case/index.js +18 -0
- package/api/v2/index.d.ts +4 -0
- package/api/v2/index.js +6 -0
- package/api/v2/search/facet.d.ts +3 -0
- package/api/v2/search/facet.js +12 -0
- package/api/v2/search/index.d.ts +5 -0
- package/api/v2/search/index.js +24 -0
- package/commons/components/leftnav/LeftNavDrawer.js +1 -1
- package/components/app/App.js +34 -7
- package/components/app/hooks/useMatchers.js +2 -2
- package/components/app/hooks/useMatchers.test.js +22 -22
- package/components/app/hooks/useTitle.js +3 -3
- package/components/app/providers/FavouritesProvider.js +2 -2
- package/components/app/providers/ParameterProvider.d.ts +9 -2
- package/components/app/providers/ParameterProvider.js +165 -240
- package/components/app/providers/ParameterProvider.test.js +307 -14
- package/components/app/providers/RecordProvider.d.ts +23 -0
- package/components/app/providers/{HitProvider.js → RecordProvider.js} +41 -41
- package/components/app/providers/{HitSearchProvider.d.ts → RecordSearchProvider.d.ts} +6 -6
- package/components/app/providers/{HitSearchProvider.js → RecordSearchProvider.js} +12 -17
- package/components/app/providers/{HitSearchProvider.test.js → RecordSearchProvider.test.js} +51 -70
- package/components/elements/ContextMenu.d.ts +56 -0
- package/components/elements/ContextMenu.js +109 -0
- package/components/elements/ContextMenu.test.js +215 -0
- package/components/{routes/overviews/OverviewEditor.js → elements/MarkdownEditor.js} +3 -3
- package/components/elements/ObjectDetails.d.ts +6 -0
- package/components/elements/{hit/HitDetails.js → ObjectDetails.js} +17 -17
- package/components/elements/PluginTypography.d.ts +2 -1
- package/components/elements/PluginTypography.js +3 -2
- package/components/elements/UserList.d.ts +5 -2
- package/components/elements/UserList.js +14 -5
- package/components/elements/addons/search/phrase/Phrase.js +1 -1
- package/components/elements/case/CaseCard.d.ts +8 -0
- package/components/elements/case/CaseCard.js +39 -0
- package/components/elements/case/CasePreview.d.ts +6 -0
- package/components/elements/case/CasePreview.js +17 -0
- package/components/elements/case/StatusIcon.d.ts +5 -0
- package/components/elements/case/StatusIcon.js +13 -0
- package/components/elements/display/ChipPopper.d.ts +1 -1
- package/components/elements/display/HowlerCard.js +1 -1
- package/components/elements/display/Modal.js +1 -0
- package/components/elements/hit/HitActions.js +4 -4
- package/components/elements/hit/HitBanner.js +28 -48
- package/components/elements/hit/HitCard.js +5 -5
- package/components/elements/hit/HitLabels.js +2 -2
- package/components/elements/hit/{HitQuickSearch.d.ts → HitPreview.d.ts} +3 -3
- package/components/elements/hit/{HitQuickSearch.js → HitPreview.js} +10 -4
- package/components/elements/hit/HitSummary.d.ts +2 -1
- package/components/elements/hit/HitSummary.js +6 -5
- package/components/elements/hit/aggregate/HitGraph.js +8 -8
- package/components/elements/hit/elements/AnalyticLink.d.ts +8 -0
- package/components/elements/hit/elements/AnalyticLink.js +22 -0
- package/components/elements/hit/outlines/DefaultOutline.js +1 -1
- package/components/elements/hit/related/RelatedRecords.js +63 -0
- package/components/elements/observable/ObservableCard.d.ts +6 -0
- package/components/elements/observable/ObservableCard.js +23 -0
- package/components/elements/observable/ObservablePreview.d.ts +6 -0
- package/components/elements/observable/ObservablePreview.js +12 -0
- package/components/elements/{hit/HitComments.d.ts → record/RecordComments.d.ts} +5 -4
- package/components/elements/{hit/HitComments.js → record/RecordComments.js} +29 -28
- package/components/{routes/hits/search/HitContextMenu.d.ts → elements/record/RecordContextMenu.d.ts} +3 -3
- package/components/elements/record/RecordContextMenu.js +235 -0
- package/components/elements/record/RecordContextMenu.test.d.ts +1 -0
- package/components/{routes/hits/search/HitContextMenu.test.js → elements/record/RecordContextMenu.test.js} +39 -39
- package/components/elements/record/RecordRelated.d.ts +7 -0
- package/components/elements/record/RecordRelated.js +34 -0
- package/components/elements/{hit/HitWorklog.d.ts → record/RecordWorklog.d.ts} +4 -3
- package/components/elements/{hit/HitWorklog.js → record/RecordWorklog.js} +15 -13
- package/components/elements/view/ViewTitle.js +1 -1
- package/components/hooks/useHitActions.d.ts +1 -1
- package/components/hooks/useHitActions.js +4 -4
- package/components/hooks/useMyPreferences.js +10 -1
- package/components/hooks/useMySearch.js +2 -2
- package/components/hooks/useMySitemap.js +4 -1
- package/components/hooks/useMyTheme.js +9 -2
- package/components/hooks/useParamState.test.js +3 -4
- package/components/hooks/{useHitSelection.d.ts → useRecordSelection.d.ts} +2 -2
- package/components/hooks/{useHitSelection.js → useRecordSelection.js} +12 -33
- package/components/hooks/useRelatedRecords.d.ts +13 -0
- package/components/hooks/useRelatedRecords.js +32 -0
- package/components/routes/action/edit/ActionEditor.js +2 -2
- package/components/routes/action/view/ActionSearch.js +1 -1
- package/components/routes/advanced/QueryBuilder.js +1 -1
- package/components/routes/advanced/QueryEditor.js +3 -3
- package/components/routes/advanced/historyCompletionProvider.js +3 -3
- package/components/routes/analytics/AnalyticDetails.js +2 -2
- package/components/routes/analytics/AnalyticSearch.js +1 -1
- package/components/routes/cases/CaseViewer.d.ts +2 -0
- package/components/routes/cases/CaseViewer.js +22 -0
- package/components/routes/cases/Cases.d.ts +2 -0
- package/components/routes/cases/Cases.js +101 -0
- package/components/routes/cases/constants.d.ts +5 -0
- package/components/routes/cases/constants.js +5 -0
- package/components/routes/cases/detail/AlertPanel.d.ts +6 -0
- package/components/routes/cases/detail/AlertPanel.js +33 -0
- package/components/routes/cases/detail/CaseAssets.d.ts +12 -0
- package/components/routes/cases/detail/CaseAssets.js +101 -0
- package/components/routes/cases/detail/CaseAssets.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseAssets.test.js +163 -0
- package/components/routes/cases/detail/CaseDashboard.d.ts +7 -0
- package/components/routes/cases/detail/CaseDashboard.js +51 -0
- package/components/routes/cases/detail/CaseDetails.d.ts +6 -0
- package/components/routes/cases/detail/CaseDetails.js +61 -0
- package/components/routes/cases/detail/CaseOverview.d.ts +7 -0
- package/components/routes/cases/detail/CaseOverview.js +43 -0
- package/components/routes/cases/detail/CaseSidebar.d.ts +6 -0
- package/components/routes/cases/detail/CaseSidebar.js +61 -0
- package/components/routes/cases/detail/CaseTask.d.ts +11 -0
- package/components/routes/cases/detail/CaseTask.js +57 -0
- package/components/routes/cases/detail/ItemPage.d.ts +6 -0
- package/components/routes/cases/detail/ItemPage.js +99 -0
- package/components/routes/cases/detail/RelatedCasePanel.d.ts +6 -0
- package/components/routes/cases/detail/RelatedCasePanel.js +31 -0
- package/components/routes/cases/detail/TaskPanel.d.ts +7 -0
- package/components/routes/cases/detail/TaskPanel.js +52 -0
- package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +12 -0
- package/components/routes/cases/detail/aggregates/CaseAggregate.js +19 -0
- package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +6 -0
- package/components/routes/cases/detail/aggregates/SourceAggregate.js +27 -0
- package/components/routes/cases/detail/assets/Asset.d.ts +14 -0
- package/components/routes/cases/detail/assets/Asset.js +12 -0
- package/components/routes/cases/detail/assets/Asset.test.d.ts +1 -0
- package/components/routes/cases/detail/assets/Asset.test.js +72 -0
- package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +13 -0
- package/components/routes/cases/detail/sidebar/CaseFolder.js +131 -0
- package/components/routes/cases/detail/sidebar/types.d.ts +3 -0
- package/components/routes/cases/detail/sidebar/utils.d.ts +3 -0
- package/components/routes/cases/detail/sidebar/utils.js +25 -0
- package/components/routes/cases/hooks/useCase.d.ts +13 -0
- package/components/routes/cases/hooks/useCase.js +38 -0
- package/components/routes/cases/modals/ResolveModal.d.ts +7 -0
- package/components/routes/cases/modals/ResolveModal.js +59 -0
- package/components/routes/dossiers/DossierEditor.js +2 -2
- package/components/routes/dossiers/DossierEditor.test.js +1 -1
- package/components/routes/help/ApiDocumentation.js +1 -1
- package/components/routes/help/HitBannerDocumentation.js +1 -0
- package/components/routes/help/HitDocumentation.js +1 -3
- package/components/routes/hits/search/InformationPane.d.ts +1 -0
- package/components/routes/hits/search/InformationPane.js +47 -60
- package/components/routes/hits/search/LayoutSettings.js +3 -3
- package/components/routes/hits/search/QuerySettings.js +2 -1
- package/components/routes/hits/search/QuerySettings.test.js +14 -9
- package/components/routes/hits/search/{HitBrowser.js → RecordBrowser.js} +9 -9
- package/components/routes/hits/search/{HitQuery.d.ts → RecordQuery.d.ts} +2 -2
- package/components/routes/hits/search/{HitQuery.js → RecordQuery.js} +6 -6
- package/components/routes/hits/search/SearchPane.js +26 -49
- package/components/routes/hits/search/ViewLink.js +3 -3
- package/components/routes/hits/search/ViewLink.test.js +8 -8
- package/components/routes/hits/search/grid/AddColumnModal.js +5 -4
- package/components/routes/hits/search/grid/EnhancedCell.d.ts +2 -1
- package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
- package/components/routes/hits/search/grid/HitGrid.js +20 -18
- package/components/routes/hits/search/grid/{HitRow.d.ts → RecordRow.d.ts} +3 -2
- package/components/routes/hits/search/grid/{HitRow.js → RecordRow.js} +10 -8
- package/components/routes/hits/search/shared/IndexPicker.d.ts +2 -0
- package/components/routes/hits/search/shared/IndexPicker.js +20 -0
- package/components/routes/hits/view/HitViewer.js +12 -13
- package/components/routes/home/ViewCard.js +4 -4
- package/components/routes/observables/ObservableViewer.d.ts +7 -0
- package/components/routes/observables/ObservableViewer.js +27 -0
- package/components/routes/overviews/OverviewViewer.js +2 -2
- package/components/routes/views/ViewComposer.js +4 -4
- package/locales/en/translation.json +65 -3
- package/locales/fr/translation.json +63 -3
- package/models/WithMetadata.d.ts +2 -1
- package/models/entities/generated/AttachmentsFile.d.ts +12 -0
- package/models/entities/generated/Case.d.ts +28 -0
- package/models/entities/generated/DestinationOriginal.d.ts +19 -0
- package/models/entities/generated/EmailAttachment.d.ts +8 -0
- package/models/entities/generated/EmailParent.d.ts +19 -0
- package/models/entities/generated/Enrichments.d.ts +7 -0
- package/models/entities/generated/EnrichmentsIndicator.d.ts +21 -0
- package/models/entities/generated/Hit.d.ts +1 -0
- package/models/entities/generated/Howler.d.ts +0 -4
- package/models/entities/generated/HttpResponse.d.ts +11 -0
- package/models/entities/generated/Item.d.ts +9 -0
- package/models/entities/generated/Observable.d.ts +85 -0
- package/models/entities/generated/ObservableCloud.d.ts +20 -0
- package/models/entities/generated/ObservableDestination.d.ts +23 -0
- package/models/entities/generated/ObservableEmail.d.ts +30 -0
- package/models/entities/generated/ObservableFile.d.ts +36 -0
- package/models/entities/generated/ObservableHowler.d.ts +43 -0
- package/models/entities/generated/ObservableHttp.d.ts +11 -0
- package/models/entities/generated/ObservableObserver.d.ts +21 -0
- package/models/entities/generated/ObservableOrganization.d.ts +7 -0
- package/models/entities/generated/ObservableProcess.d.ts +34 -0
- package/models/entities/generated/ObservableSource.d.ts +23 -0
- package/models/entities/generated/ObservableThreat.d.ts +21 -0
- package/models/entities/generated/ObservableTls.d.ts +12 -0
- package/models/entities/generated/ObserverIngress.d.ts +9 -0
- package/models/entities/generated/Rule.d.ts +2 -10
- package/models/entities/generated/Task.d.ts +10 -0
- package/models/entities/generated/Threat.d.ts +2 -2
- package/models/entities/generated/{Enrichment.d.ts → ThreatEnrichment.d.ts} +1 -1
- package/package.json +121 -104
- package/plugins/clue/components/ClueTypography.js +2 -2
- package/plugins/clue/utils.d.ts +2 -1
- package/tests/utils.d.ts +2 -0
- package/tests/utils.js +8 -0
- package/utils/constants.d.ts +3 -3
- package/utils/hitFunctions.d.ts +2 -1
- package/utils/hitFunctions.js +4 -4
- package/utils/typeUtils.d.ts +7 -0
- package/utils/typeUtils.js +27 -0
- package/components/app/providers/HitProvider.d.ts +0 -22
- package/components/elements/display/icons/BundleButton.d.ts +0 -6
- package/components/elements/display/icons/BundleButton.js +0 -32
- package/components/elements/hit/HitRelated.d.ts +0 -6
- package/components/elements/hit/HitRelated.js +0 -7
- package/components/routes/help/BundleDocumentation.d.ts +0 -3
- package/components/routes/help/BundleDocumentation.js +0 -12
- package/components/routes/help/markdown/en/bundles.md.js +0 -1
- package/components/routes/help/markdown/fr/bundles.md.js +0 -1
- package/components/routes/hits/search/BundleParentMenu.d.ts +0 -6
- package/components/routes/hits/search/BundleParentMenu.js +0 -32
- package/components/routes/hits/search/BundleScroller.d.ts +0 -2
- package/components/routes/hits/search/BundleScroller.js +0 -6
- package/components/routes/hits/search/HitContextMenu.js +0 -227
- /package/components/app/providers/{HitSearchProvider.test.d.ts → RecordSearchProvider.test.d.ts} +0 -0
- /package/components/{routes/hits/search/HitContextMenu.test.d.ts → elements/ContextMenu.test.d.ts} +0 -0
- /package/components/{routes/overviews/OverviewEditor.d.ts → elements/MarkdownEditor.d.ts} +0 -0
- /package/components/elements/hit/{HitDetails.d.ts → related/RelatedRecords.d.ts} +0 -0
- /package/components/routes/hits/search/{HitBrowser.d.ts → RecordBrowser.d.ts} +0 -0
|
@@ -272,17 +272,17 @@ describe('ParameterContext', () => {
|
|
|
272
272
|
});
|
|
273
273
|
});
|
|
274
274
|
});
|
|
275
|
-
describe('
|
|
275
|
+
describe('resetFilters', () => {
|
|
276
276
|
it('should clear all filters', async () => {
|
|
277
277
|
mockSearchParams = new URLSearchParams();
|
|
278
278
|
mockSearchParams.append('filter', 'filter1');
|
|
279
279
|
mockSearchParams.append('filter', 'filter2');
|
|
280
280
|
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
281
281
|
filters: ctx.filters,
|
|
282
|
-
|
|
282
|
+
resetFilters: ctx.resetFilters
|
|
283
283
|
})), { wrapper: Wrapper });
|
|
284
284
|
await act(async () => {
|
|
285
|
-
hook.result.current.
|
|
285
|
+
hook.result.current.resetFilters();
|
|
286
286
|
});
|
|
287
287
|
await waitFor(() => {
|
|
288
288
|
expect(hook.result.current.filters).toEqual([]);
|
|
@@ -291,10 +291,10 @@ describe('ParameterContext', () => {
|
|
|
291
291
|
it('should be no-op when already empty', async () => {
|
|
292
292
|
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
293
293
|
filters: ctx.filters,
|
|
294
|
-
|
|
294
|
+
resetFilters: ctx.resetFilters
|
|
295
295
|
})), { wrapper: Wrapper });
|
|
296
296
|
await act(async () => {
|
|
297
|
-
hook.result.current.
|
|
297
|
+
hook.result.current.resetFilters();
|
|
298
298
|
});
|
|
299
299
|
await waitFor(() => {
|
|
300
300
|
expect(hook.result.current.filters).toEqual([]);
|
|
@@ -441,10 +441,10 @@ describe('ParameterContext', () => {
|
|
|
441
441
|
mockSearchParams.append('filter', 'filter1');
|
|
442
442
|
mockSearchParams.append('filter', 'filter2');
|
|
443
443
|
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
444
|
-
|
|
444
|
+
resetFilters: ctx.resetFilters
|
|
445
445
|
})), { wrapper: Wrapper });
|
|
446
446
|
await act(async () => {
|
|
447
|
-
hook.result.current.
|
|
447
|
+
hook.result.current.resetFilters();
|
|
448
448
|
});
|
|
449
449
|
await waitFor(() => {
|
|
450
450
|
expect(mockSetParams).toHaveBeenCalled();
|
|
@@ -706,6 +706,299 @@ describe('ParameterContext', () => {
|
|
|
706
706
|
});
|
|
707
707
|
});
|
|
708
708
|
});
|
|
709
|
+
describe('indexes (multi-index support)', () => {
|
|
710
|
+
it('should initialize with default ["hit"] when no index params are present', async () => {
|
|
711
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ctx.indexes), { wrapper: Wrapper });
|
|
712
|
+
expect(hook.result.current).toEqual(['hit']);
|
|
713
|
+
});
|
|
714
|
+
it('should initialize with single index from URL', async () => {
|
|
715
|
+
mockSearchParams = new URLSearchParams({ index: 'observable' });
|
|
716
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ctx.indexes), { wrapper: Wrapper });
|
|
717
|
+
expect(hook.result.current).toEqual(['observable']);
|
|
718
|
+
});
|
|
719
|
+
it('should initialize with multiple indexes from URL', async () => {
|
|
720
|
+
mockSearchParams = new URLSearchParams();
|
|
721
|
+
mockSearchParams.append('index', 'hit');
|
|
722
|
+
mockSearchParams.append('index', 'observable');
|
|
723
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ctx.indexes), { wrapper: Wrapper });
|
|
724
|
+
expect(hook.result.current).toEqual(['hit', 'observable']);
|
|
725
|
+
});
|
|
726
|
+
it('should deduplicate repeated index values from URL', async () => {
|
|
727
|
+
mockSearchParams = new URLSearchParams();
|
|
728
|
+
mockSearchParams.append('index', 'hit');
|
|
729
|
+
mockSearchParams.append('index', 'hit');
|
|
730
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ctx.indexes), { wrapper: Wrapper });
|
|
731
|
+
expect(hook.result.current).toEqual(['hit']);
|
|
732
|
+
});
|
|
733
|
+
describe('addIndex', () => {
|
|
734
|
+
it('should add an index to the default array', async () => {
|
|
735
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
736
|
+
indexes: ctx.indexes,
|
|
737
|
+
addIndex: ctx.addIndex
|
|
738
|
+
})), { wrapper: Wrapper });
|
|
739
|
+
await act(async () => {
|
|
740
|
+
hook.result.current.addIndex('observable');
|
|
741
|
+
});
|
|
742
|
+
await waitFor(() => {
|
|
743
|
+
expect(hook.result.current.indexes).toEqual(['hit', 'observable']);
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
it('should not add a duplicate index', async () => {
|
|
747
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
748
|
+
indexes: ctx.indexes,
|
|
749
|
+
addIndex: ctx.addIndex
|
|
750
|
+
})), { wrapper: Wrapper });
|
|
751
|
+
await act(async () => {
|
|
752
|
+
hook.result.current.addIndex('hit');
|
|
753
|
+
});
|
|
754
|
+
await waitFor(() => {
|
|
755
|
+
expect(hook.result.current.indexes).toEqual(['hit']);
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
});
|
|
759
|
+
describe('removeIndex', () => {
|
|
760
|
+
it('should remove an index from the list', async () => {
|
|
761
|
+
mockSearchParams = new URLSearchParams();
|
|
762
|
+
mockSearchParams.append('index', 'hit');
|
|
763
|
+
mockSearchParams.append('index', 'observable');
|
|
764
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
765
|
+
indexes: ctx.indexes,
|
|
766
|
+
removeIndex: ctx.removeIndex
|
|
767
|
+
})), { wrapper: Wrapper });
|
|
768
|
+
await act(async () => {
|
|
769
|
+
hook.result.current.removeIndex('hit');
|
|
770
|
+
});
|
|
771
|
+
await waitFor(() => {
|
|
772
|
+
expect(hook.result.current.indexes).toEqual(['observable']);
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
it('should do nothing when removing a nonexistent index', async () => {
|
|
776
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
777
|
+
indexes: ctx.indexes,
|
|
778
|
+
removeIndex: ctx.removeIndex
|
|
779
|
+
})), { wrapper: Wrapper });
|
|
780
|
+
await act(async () => {
|
|
781
|
+
hook.result.current.removeIndex('observable');
|
|
782
|
+
});
|
|
783
|
+
await waitFor(() => {
|
|
784
|
+
expect(hook.result.current.indexes).toEqual(['hit']);
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
it('should handle removing from empty array', async () => {
|
|
788
|
+
mockSearchParams = new URLSearchParams();
|
|
789
|
+
mockSearchParams.append('index', 'hit');
|
|
790
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
791
|
+
indexes: ctx.indexes,
|
|
792
|
+
removeIndex: ctx.removeIndex
|
|
793
|
+
})), { wrapper: Wrapper });
|
|
794
|
+
await act(async () => {
|
|
795
|
+
hook.result.current.removeIndex('hit');
|
|
796
|
+
});
|
|
797
|
+
await waitFor(() => {
|
|
798
|
+
expect(hook.result.current.indexes).toEqual([]);
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
describe('setIndex', () => {
|
|
803
|
+
it('should update the index at the specified position', async () => {
|
|
804
|
+
mockSearchParams = new URLSearchParams();
|
|
805
|
+
mockSearchParams.append('index', 'hit');
|
|
806
|
+
mockSearchParams.append('index', 'observable');
|
|
807
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
808
|
+
indexes: ctx.indexes,
|
|
809
|
+
setIndex: ctx.setIndex
|
|
810
|
+
})), { wrapper: Wrapper });
|
|
811
|
+
await act(async () => {
|
|
812
|
+
hook.result.current.setIndex(0, 'observable');
|
|
813
|
+
});
|
|
814
|
+
await waitFor(() => {
|
|
815
|
+
expect(hook.result.current.indexes).toEqual(['observable', 'observable']);
|
|
816
|
+
});
|
|
817
|
+
});
|
|
818
|
+
it('should do nothing when index is out of bounds', async () => {
|
|
819
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
820
|
+
indexes: ctx.indexes,
|
|
821
|
+
setIndex: ctx.setIndex
|
|
822
|
+
})), { wrapper: Wrapper });
|
|
823
|
+
await act(async () => {
|
|
824
|
+
hook.result.current.setIndex(5, 'observable');
|
|
825
|
+
});
|
|
826
|
+
await waitFor(() => {
|
|
827
|
+
expect(hook.result.current.indexes).toEqual(['hit']);
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
it('should do nothing when position is negative', async () => {
|
|
831
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
832
|
+
indexes: ctx.indexes,
|
|
833
|
+
setIndex: ctx.setIndex
|
|
834
|
+
})), { wrapper: Wrapper });
|
|
835
|
+
await act(async () => {
|
|
836
|
+
hook.result.current.setIndex(-1, 'observable');
|
|
837
|
+
});
|
|
838
|
+
await waitFor(() => {
|
|
839
|
+
expect(hook.result.current.indexes).toEqual(['hit']);
|
|
840
|
+
});
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
describe('setIndexes', () => {
|
|
844
|
+
it('should replace all indexes with the provided list', async () => {
|
|
845
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
846
|
+
indexes: ctx.indexes,
|
|
847
|
+
setIndexes: ctx.setIndexes
|
|
848
|
+
})), { wrapper: Wrapper });
|
|
849
|
+
await act(async () => {
|
|
850
|
+
hook.result.current.setIndexes(['observable']);
|
|
851
|
+
});
|
|
852
|
+
await waitFor(() => {
|
|
853
|
+
expect(hook.result.current.indexes).toEqual(['observable']);
|
|
854
|
+
});
|
|
855
|
+
});
|
|
856
|
+
it('should deduplicate values when setting all indexes', async () => {
|
|
857
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
858
|
+
indexes: ctx.indexes,
|
|
859
|
+
setIndexes: ctx.setIndexes
|
|
860
|
+
})), { wrapper: Wrapper });
|
|
861
|
+
await act(async () => {
|
|
862
|
+
hook.result.current.setIndexes(['hit', 'hit', 'observable']);
|
|
863
|
+
});
|
|
864
|
+
await waitFor(() => {
|
|
865
|
+
expect(hook.result.current.indexes).toEqual(['hit', 'observable']);
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
it('should set to empty array', async () => {
|
|
869
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
870
|
+
indexes: ctx.indexes,
|
|
871
|
+
setIndexes: ctx.setIndexes
|
|
872
|
+
})), { wrapper: Wrapper });
|
|
873
|
+
await act(async () => {
|
|
874
|
+
hook.result.current.setIndexes([]);
|
|
875
|
+
});
|
|
876
|
+
await waitFor(() => {
|
|
877
|
+
expect(hook.result.current.indexes).toEqual([]);
|
|
878
|
+
});
|
|
879
|
+
});
|
|
880
|
+
});
|
|
881
|
+
describe('resetIndexes', () => {
|
|
882
|
+
it('should reset indexes to default ["hit"]', async () => {
|
|
883
|
+
mockSearchParams = new URLSearchParams();
|
|
884
|
+
mockSearchParams.append('index', 'hit');
|
|
885
|
+
mockSearchParams.append('index', 'observable');
|
|
886
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
887
|
+
indexes: ctx.indexes,
|
|
888
|
+
resetIndexes: ctx.resetIndexes
|
|
889
|
+
})), { wrapper: Wrapper });
|
|
890
|
+
await act(async () => {
|
|
891
|
+
hook.result.current.resetIndexes();
|
|
892
|
+
});
|
|
893
|
+
await waitFor(() => {
|
|
894
|
+
expect(hook.result.current.indexes).toEqual(['hit']);
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
it('should reset to default even when called on empty array', async () => {
|
|
898
|
+
mockSearchParams = new URLSearchParams();
|
|
899
|
+
mockSearchParams.append('index', 'hit');
|
|
900
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
901
|
+
indexes: ctx.indexes,
|
|
902
|
+
removeIndex: ctx.removeIndex,
|
|
903
|
+
resetIndexes: ctx.resetIndexes
|
|
904
|
+
})), { wrapper: Wrapper });
|
|
905
|
+
// First empty it
|
|
906
|
+
await act(async () => {
|
|
907
|
+
hook.result.current.removeIndex('hit');
|
|
908
|
+
});
|
|
909
|
+
await waitFor(() => {
|
|
910
|
+
expect(hook.result.current.indexes).toEqual([]);
|
|
911
|
+
});
|
|
912
|
+
// Resetting always returns to default ['hit']
|
|
913
|
+
await act(async () => {
|
|
914
|
+
hook.result.current.resetIndexes();
|
|
915
|
+
});
|
|
916
|
+
await waitFor(() => {
|
|
917
|
+
expect(hook.result.current.indexes).toEqual(['hit']);
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
});
|
|
921
|
+
describe('URL synchronization', () => {
|
|
922
|
+
it('should not write the default ["hit"] index to the URL', async () => {
|
|
923
|
+
renderHook(() => useContextSelector(ParameterContext, ctx => ctx.indexes), { wrapper: Wrapper });
|
|
924
|
+
// Allow any effects to flush
|
|
925
|
+
await waitFor(() => {
|
|
926
|
+
// If setParams was called, the URL must not contain ?index=hit
|
|
927
|
+
if (mockSetParams.mock.calls.length > 0) {
|
|
928
|
+
const call = mockSetParams.mock.calls[mockSetParams.mock.calls.length - 1];
|
|
929
|
+
const urlParams = typeof call[0] === 'function' ? call[0](mockSearchParams) : call[0];
|
|
930
|
+
expect(urlParams.getAll('index')).toEqual([]);
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
// setParams not called at all is also fine
|
|
934
|
+
expect(true).toBe(true);
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
});
|
|
938
|
+
it('should write a non-default index to the URL', async () => {
|
|
939
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
940
|
+
addIndex: ctx.addIndex,
|
|
941
|
+
resetIndexes: ctx.resetIndexes
|
|
942
|
+
})), { wrapper: Wrapper });
|
|
943
|
+
await act(async () => {
|
|
944
|
+
hook.result.current.resetIndexes();
|
|
945
|
+
hook.result.current.addIndex('observable');
|
|
946
|
+
});
|
|
947
|
+
await waitFor(() => {
|
|
948
|
+
expect(mockSetParams).toHaveBeenCalled();
|
|
949
|
+
const call = mockSetParams.mock.calls[mockSetParams.mock.calls.length - 1];
|
|
950
|
+
const urlParams = typeof call[0] === 'function' ? call[0](mockSearchParams) : call[0];
|
|
951
|
+
expect(urlParams.getAll('index')).toEqual(['hit', 'observable']);
|
|
952
|
+
});
|
|
953
|
+
});
|
|
954
|
+
it('should sync multiple indexes to URL as multiple index params', async () => {
|
|
955
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
956
|
+
addIndex: ctx.addIndex
|
|
957
|
+
})), { wrapper: Wrapper });
|
|
958
|
+
await act(async () => {
|
|
959
|
+
hook.result.current.addIndex('observable');
|
|
960
|
+
});
|
|
961
|
+
await waitFor(() => {
|
|
962
|
+
expect(mockSetParams).toHaveBeenCalled();
|
|
963
|
+
const call = mockSetParams.mock.calls[mockSetParams.mock.calls.length - 1];
|
|
964
|
+
const urlParams = typeof call[0] === 'function' ? call[0](mockSearchParams) : call[0];
|
|
965
|
+
expect(urlParams.getAll('index')).toEqual(['hit', 'observable']);
|
|
966
|
+
});
|
|
967
|
+
});
|
|
968
|
+
it('should remove all index params from URL when state resets to default', async () => {
|
|
969
|
+
mockSearchParams = new URLSearchParams();
|
|
970
|
+
mockSearchParams.append('index', 'hit');
|
|
971
|
+
mockSearchParams.append('index', 'observable');
|
|
972
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
973
|
+
resetIndexes: ctx.resetIndexes
|
|
974
|
+
})), { wrapper: Wrapper });
|
|
975
|
+
await act(async () => {
|
|
976
|
+
hook.result.current.resetIndexes();
|
|
977
|
+
});
|
|
978
|
+
await waitFor(() => {
|
|
979
|
+
expect(mockSetParams).toHaveBeenCalled();
|
|
980
|
+
const call = mockSetParams.mock.calls[mockSetParams.mock.calls.length - 1];
|
|
981
|
+
const urlParams = typeof call[0] === 'function' ? call[0](mockSearchParams) : call[0];
|
|
982
|
+
expect(urlParams.getAll('index')).toEqual([]);
|
|
983
|
+
});
|
|
984
|
+
});
|
|
985
|
+
it('should remove index param from URL when state returns to default', async () => {
|
|
986
|
+
mockSearchParams = new URLSearchParams({ index: 'observable' });
|
|
987
|
+
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
988
|
+
setIndexes: ctx.setIndexes
|
|
989
|
+
})), { wrapper: Wrapper });
|
|
990
|
+
await act(async () => {
|
|
991
|
+
hook.result.current.setIndexes(['hit']);
|
|
992
|
+
});
|
|
993
|
+
await waitFor(() => {
|
|
994
|
+
expect(mockSetParams).toHaveBeenCalled();
|
|
995
|
+
const call = mockSetParams.mock.calls[mockSetParams.mock.calls.length - 1];
|
|
996
|
+
const urlParams = typeof call[0] === 'function' ? call[0](mockSearchParams) : call[0];
|
|
997
|
+
expect(urlParams.getAll('index')).toEqual([]);
|
|
998
|
+
});
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
709
1002
|
describe('views (multi-view support)', () => {
|
|
710
1003
|
it('should initialize with empty array when no view params present', async () => {
|
|
711
1004
|
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ctx.views), { wrapper: Wrapper });
|
|
@@ -839,17 +1132,17 @@ describe('ParameterContext', () => {
|
|
|
839
1132
|
});
|
|
840
1133
|
});
|
|
841
1134
|
});
|
|
842
|
-
describe('
|
|
1135
|
+
describe('resetViews', () => {
|
|
843
1136
|
it('should clear all views', async () => {
|
|
844
1137
|
mockSearchParams = new URLSearchParams();
|
|
845
1138
|
mockSearchParams.append('view', 'view_1');
|
|
846
1139
|
mockSearchParams.append('view', 'view_2');
|
|
847
1140
|
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
848
1141
|
views: ctx.views,
|
|
849
|
-
|
|
1142
|
+
resetViews: ctx.resetViews
|
|
850
1143
|
})), { wrapper: Wrapper });
|
|
851
1144
|
await act(async () => {
|
|
852
|
-
hook.result.current.
|
|
1145
|
+
hook.result.current.resetViews();
|
|
853
1146
|
});
|
|
854
1147
|
await waitFor(() => {
|
|
855
1148
|
expect(hook.result.current.views).toEqual([]);
|
|
@@ -858,10 +1151,10 @@ describe('ParameterContext', () => {
|
|
|
858
1151
|
it('should be no-op when already empty', async () => {
|
|
859
1152
|
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
860
1153
|
views: ctx.views,
|
|
861
|
-
|
|
1154
|
+
resetViews: ctx.resetViews
|
|
862
1155
|
})), { wrapper: Wrapper });
|
|
863
1156
|
await act(async () => {
|
|
864
|
-
hook.result.current.
|
|
1157
|
+
hook.result.current.resetViews();
|
|
865
1158
|
});
|
|
866
1159
|
await waitFor(() => {
|
|
867
1160
|
expect(hook.result.current.views).toEqual([]);
|
|
@@ -1008,10 +1301,10 @@ describe('ParameterContext', () => {
|
|
|
1008
1301
|
mockSearchParams.append('view', 'view_1');
|
|
1009
1302
|
mockSearchParams.append('view', 'view_2');
|
|
1010
1303
|
const hook = renderHook(() => useContextSelector(ParameterContext, ctx => ({
|
|
1011
|
-
|
|
1304
|
+
resetViews: ctx.resetViews
|
|
1012
1305
|
})), { wrapper: Wrapper });
|
|
1013
1306
|
await act(async () => {
|
|
1014
|
-
hook.result.current.
|
|
1307
|
+
hook.result.current.resetViews();
|
|
1015
1308
|
});
|
|
1016
1309
|
await waitFor(() => {
|
|
1017
1310
|
expect(mockSetParams).toHaveBeenCalled();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
2
|
+
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
3
|
+
import type { WithMetadata } from '@cccsaurora/howler-ui/models/WithMetadata';
|
|
4
|
+
import type { FC, PropsWithChildren } from 'react';
|
|
5
|
+
export interface RecordContextType {
|
|
6
|
+
records: {
|
|
7
|
+
[index: string]: Hit | Observable;
|
|
8
|
+
};
|
|
9
|
+
selectedRecords: (Hit | Observable)[];
|
|
10
|
+
addRecordToSelection: (id: string) => void;
|
|
11
|
+
removeRecordFromSelection: (id: string) => void;
|
|
12
|
+
clearSelectedRecords: (except?: string) => void;
|
|
13
|
+
loadRecords: (hits: (Hit | Observable)[]) => void;
|
|
14
|
+
updateRecord: (newHit: Hit | Observable) => void;
|
|
15
|
+
getRecord: (id: string, force?: boolean) => Promise<WithMetadata<Hit | Observable>>;
|
|
16
|
+
}
|
|
17
|
+
export declare const RecordContext: import("use-context-selector").Context<RecordContextType>;
|
|
18
|
+
/**
|
|
19
|
+
* Central repository for storing individual hit data across the application. Allows efficient retrieval of hits across componenents.
|
|
20
|
+
*/
|
|
21
|
+
declare const RecordProvider: FC<PropsWithChildren>;
|
|
22
|
+
export declare const useHitContextSelector: <Selected>(selector: (value: RecordContextType) => Selected) => Selected;
|
|
23
|
+
export default RecordProvider;
|
|
@@ -5,25 +5,25 @@ import { uniq } from 'lodash-es';
|
|
|
5
5
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
6
|
import { createContext, useContextSelector } from 'use-context-selector';
|
|
7
7
|
import { SocketContext } from './SocketProvider';
|
|
8
|
-
export const
|
|
8
|
+
export const RecordContext = createContext(null);
|
|
9
9
|
/**
|
|
10
10
|
* Central repository for storing individual hit data across the application. Allows efficient retrieval of hits across componenents.
|
|
11
11
|
*/
|
|
12
|
-
const
|
|
12
|
+
const RecordProvider = ({ children }) => {
|
|
13
13
|
const { dispatchApi } = useMyApi();
|
|
14
14
|
const { addListener, removeListener } = useContext(SocketContext);
|
|
15
15
|
/**
|
|
16
16
|
* The most immediate of two levels of caching, this ref stores the raw promises for each hit.
|
|
17
17
|
* Rapidly updates, good for uses in-context where parallel updates my be occurring, i.e.
|
|
18
|
-
* when two cards request the same hit that's missing from the store. Used in
|
|
18
|
+
* when two cards request the same hit that's missing from the store. Used in getRecord for this reason.
|
|
19
19
|
*/
|
|
20
|
-
const
|
|
20
|
+
const recordRequests = useRef({});
|
|
21
21
|
/**
|
|
22
22
|
* The "Authoritative" store of hits. Changes here will trigger rerenders, and essentially
|
|
23
23
|
* caches the result of the above promises. Slower to update, so used outside of the hitcontext
|
|
24
24
|
* where parallel requests aren't an issue.
|
|
25
25
|
*/
|
|
26
|
-
const [
|
|
26
|
+
const [records, setRecords] = useState({});
|
|
27
27
|
const [selectedHitIds, setSelectedHitIds] = useState([]);
|
|
28
28
|
// The central location where we propagate any changes from hits made via websockets into the repository. We just save every update,
|
|
29
29
|
// instead of caching it across many components inconsistently as before.
|
|
@@ -31,8 +31,8 @@ const HitProvider = ({ children }) => {
|
|
|
31
31
|
if (data.hit) {
|
|
32
32
|
// eslint-disable-next-line no-console
|
|
33
33
|
console.debug('Received websocket update for hit', data.hit.howler.id);
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
recordRequests.current[data.hit.howler.id] = Promise.resolve(data.hit);
|
|
35
|
+
setRecords(_hits => ({
|
|
36
36
|
..._hits,
|
|
37
37
|
[data.hit.howler.id]: {
|
|
38
38
|
..._hits[data.hit.howler.id],
|
|
@@ -46,68 +46,68 @@ const HitProvider = ({ children }) => {
|
|
|
46
46
|
return () => removeListener('hits');
|
|
47
47
|
}, [addListener, handler, removeListener]);
|
|
48
48
|
/**
|
|
49
|
-
* A method to retrieve a
|
|
49
|
+
* A method to retrieve a record from the context. It first checks the hit state,
|
|
50
50
|
* then checks for ongoing requests, then finally executes a new request if necessary.
|
|
51
51
|
*/
|
|
52
|
-
const
|
|
53
|
-
if (!
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
52
|
+
const getRecord = useCallback(async (id, force = false) => {
|
|
53
|
+
if (!recordRequests.current[id] || force) {
|
|
54
|
+
recordRequests.current[id] = dispatchApi(api.hit.get(id, ['template', 'dossiers', 'analytic', 'overview']));
|
|
55
|
+
const newRecord = await recordRequests.current[id];
|
|
56
|
+
setRecords(_records => ({ ..._records, [id]: newRecord }));
|
|
57
57
|
}
|
|
58
|
-
return
|
|
58
|
+
return recordRequests.current[id];
|
|
59
59
|
}, [dispatchApi]);
|
|
60
60
|
/**
|
|
61
61
|
* Update a hit in the context locally
|
|
62
62
|
*/
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const updateRecord = useCallback((newHit) => {
|
|
64
|
+
recordRequests.current[newHit.howler.id] = Promise.resolve(newHit);
|
|
65
|
+
setRecords(_hits => ({ ..._hits, [newHit.howler.id]: newHit }));
|
|
66
66
|
}, []);
|
|
67
67
|
/**
|
|
68
68
|
* Add a large number of hits to the cache. Used for results of searches.
|
|
69
69
|
*/
|
|
70
|
-
const
|
|
70
|
+
const loadRecords = useCallback((newHits) => {
|
|
71
71
|
const mappedHits = newHits.map(hit => [hit.howler.id, hit]);
|
|
72
72
|
mappedHits.forEach(([id, hit]) => {
|
|
73
|
-
|
|
73
|
+
recordRequests.current[id] = Promise.resolve(hit);
|
|
74
74
|
});
|
|
75
|
-
|
|
75
|
+
setRecords(_hits => ({ ..._hits, ...Object.fromEntries(mappedHits) }));
|
|
76
76
|
}, []);
|
|
77
|
-
const
|
|
77
|
+
const addRecordToSelection = useCallback((id) => {
|
|
78
78
|
setSelectedHitIds(_selected => uniq([..._selected, id]));
|
|
79
79
|
}, []);
|
|
80
|
-
const
|
|
80
|
+
const removeRecordFromSelection = useCallback((id) => {
|
|
81
81
|
setSelectedHitIds(_selected => _selected.filter(_id => _id !== id));
|
|
82
82
|
}, []);
|
|
83
|
-
const
|
|
83
|
+
const clearSelectedRecords = useCallback((except) => {
|
|
84
84
|
setSelectedHitIds(!!except ? [except] : []);
|
|
85
85
|
}, []);
|
|
86
|
-
const
|
|
86
|
+
const selectedRecords = useMemo(() => selectedHitIds.map(id => records[id]).filter(hit => !!hit), [records, selectedHitIds]);
|
|
87
87
|
useEffect(() => {
|
|
88
88
|
selectedHitIds.forEach(id => {
|
|
89
|
-
if (!
|
|
90
|
-
|
|
89
|
+
if (!recordRequests.current[id]) {
|
|
90
|
+
getRecord(id);
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
|
-
}, [
|
|
93
|
+
}, [getRecord, selectedHitIds]);
|
|
94
94
|
useEffect(() => {
|
|
95
|
-
Object.entries(
|
|
96
|
-
|
|
95
|
+
Object.entries(records).forEach(([id, hit]) => {
|
|
96
|
+
recordRequests.current[id] = Promise.resolve(hit);
|
|
97
97
|
});
|
|
98
|
-
}, [
|
|
99
|
-
return (_jsx(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
}, [records]);
|
|
99
|
+
return (_jsx(RecordContext.Provider, { value: {
|
|
100
|
+
records,
|
|
101
|
+
getRecord,
|
|
102
|
+
updateRecord,
|
|
103
|
+
selectedRecords,
|
|
104
|
+
addRecordToSelection,
|
|
105
|
+
removeRecordFromSelection,
|
|
106
|
+
clearSelectedRecords,
|
|
107
|
+
loadRecords
|
|
108
108
|
}, children: children }));
|
|
109
109
|
};
|
|
110
110
|
export const useHitContextSelector = (selector) => {
|
|
111
|
-
return useContextSelector(
|
|
111
|
+
return useContextSelector(RecordContext, selector);
|
|
112
112
|
};
|
|
113
|
-
export default
|
|
113
|
+
export default RecordProvider;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
|
|
2
2
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
3
3
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
4
|
+
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
4
5
|
import type { WithMetadata } from '@cccsaurora/howler-ui/models/WithMetadata';
|
|
5
6
|
import { type Dispatch, type FC, type PropsWithChildren, type SetStateAction } from 'react';
|
|
6
7
|
export interface QueryEntry {
|
|
7
8
|
[query: string]: string;
|
|
8
9
|
}
|
|
9
|
-
export interface
|
|
10
|
+
export interface RecordSearchContextType {
|
|
10
11
|
displayType: 'list' | 'grid';
|
|
11
12
|
searching: boolean;
|
|
12
13
|
error: string | null;
|
|
13
|
-
response: HowlerSearchResponse<WithMetadata<Hit>> | null;
|
|
14
|
-
bundleId: string | null;
|
|
14
|
+
response: HowlerSearchResponse<WithMetadata<Hit | Observable>> | null;
|
|
15
15
|
fzfSearch: boolean;
|
|
16
16
|
setDisplayType: (type: 'list' | 'grid') => void;
|
|
17
17
|
setFzfSearch: Dispatch<SetStateAction<boolean>>;
|
|
@@ -20,6 +20,6 @@ export interface HitSearchContextType {
|
|
|
20
20
|
queryHistory: QueryEntry;
|
|
21
21
|
setQueryHistory: ReturnType<typeof useMyLocalStorageItem>[1];
|
|
22
22
|
}
|
|
23
|
-
export declare const
|
|
24
|
-
declare const
|
|
25
|
-
export default
|
|
23
|
+
export declare const RecordSearchContext: import("use-context-selector").Context<RecordSearchContextType>;
|
|
24
|
+
declare const RecordSearchProvider: FC<PropsWithChildren>;
|
|
25
|
+
export default RecordSearchProvider;
|