Skip to content

Commit

Permalink
feat(headless): breadcrumb manager facet exclusion (#3043)
Browse files Browse the repository at this point in the history
* First pass

https://coveord.atlassian.net/browse/KIT-2563

* UTs and further improving breadcrumb-manager controller

https://coveord.atlassian.net/browse/KIT-2563

* Add static filter breadcrumb exclusion support

https://coveord.atlassian.net/browse/KIT-2563

* Further improve UTs, support insight breadcrumb manager

https://coveord.atlassian.net/browse/KIT-2563

* Remove outdated TODO

https://coveord.atlassian.net/browse/KIT-2563

* Lint

https://coveord.atlassian.net/browse/KIT-2563

* Move method config to its own type

https://coveord.atlassian.net/browse/KIT-2563
  • Loading branch information
dmbrooke authored Jul 25, 2023
1 parent a44f69d commit 21d8259
Show file tree
Hide file tree
Showing 8 changed files with 564 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface ResultChildrenProps {
getNoResultText: () => string;
getDisplayConfig: () => DisplayConfig;
getImageSize: () => ResultDisplayImageSize | undefined;
renderChild: (child: FoldedResult, isLast: boolean) => any;
renderChild: (child: FoldedResult, isLast: boolean) => VNode;
setInitialChildren: (initialChildren: FoldedResult[]) => void;
toggleShowInitialChildren: () => void;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,31 @@ import {deselectAllCategoryFacetValues} from '../../features/facets/category-fac
import {categoryFacetSetReducer as categoryFacetSet} from '../../features/facets/category-facet-set/category-facet-set-slice';
import {CategoryFacetValue} from '../../features/facets/category-facet-set/interfaces/response';
import {
toggleExcludeFacetValue,
toggleSelectFacetValue,
updateFreezeCurrentValues,
} from '../../features/facets/facet-set/facet-set-actions';
import {facetSetReducer as facetSet} from '../../features/facets/facet-set/facet-set-slice';
import {FacetValue} from '../../features/facets/facet-set/interfaces/response';
import {toggleSelectDateFacetValue} from '../../features/facets/range-facets/date-facet-set/date-facet-actions';
import {
toggleExcludeDateFacetValue,
toggleSelectDateFacetValue,
} from '../../features/facets/range-facets/date-facet-set/date-facet-actions';
import {dateFacetSetReducer as dateFacetSet} from '../../features/facets/range-facets/date-facet-set/date-facet-set-slice';
import {DateFacetValue} from '../../features/facets/range-facets/date-facet-set/interfaces/response';
import {NumericFacetValue} from '../../features/facets/range-facets/numeric-facet-set/interfaces/response';
import {toggleSelectNumericFacetValue} from '../../features/facets/range-facets/numeric-facet-set/numeric-facet-actions';
import {
toggleExcludeNumericFacetValue,
toggleSelectNumericFacetValue,
} from '../../features/facets/range-facets/numeric-facet-set/numeric-facet-actions';
import {numericFacetSetReducer as numericFacetSet} from '../../features/facets/range-facets/numeric-facet-set/numeric-facet-set-slice';
import {executeSearch} from '../../features/search/search-actions';
import {searchReducer as search} from '../../features/search/search-slice';
import {getSearchInitialState} from '../../features/search/search-state';
import {toggleSelectStaticFilterValue} from '../../features/static-filter-set/static-filter-set-actions';
import {
toggleExcludeStaticFilterValue,
toggleSelectStaticFilterValue,
} from '../../features/static-filter-set/static-filter-set-actions';
import {SearchAppState} from '../../state/search-app-state';
import {
buildMockSearchAppEngine,
Expand Down Expand Up @@ -146,7 +156,32 @@ describe('headless breadcrumb manager', () => {
);
});

it('dispatches a toggleSelectFacetValue action when #deselectBreadcrumb is called', () => {
it('dispatches an executeSearch action on exclusion', () => {
facetBreadcrumbs[0].values[0].deselect();
expect(engine.findAsyncAction(executeSearch.pending)).toBeTruthy();
});

it('dispatches an toggleExcludeFacetValue action on exclusion', () => {
facetBreadcrumbs[0].values[0].deselect();
expect(engine.actions).toContainEqual(
toggleSelectFacetValue({
facetId,
selection: mockSelectedValue,
})
);
});

it('dispatches an updateFreezeCurrentValues action on exclusion', () => {
facetBreadcrumbs[0].values[0].deselect();
expect(engine.actions).toContainEqual(
updateFreezeCurrentValues({
facetId,
freezeCurrentValues: false,
})
);
});

it('dispatches a toggleSelectFacetValue action when #deselectBreadcrumb is called for a selected facet value', () => {
breadcrumbManager.deselectBreadcrumb(facetBreadcrumbs[0].values[0]);
expect(engine.actions).toContainEqual(
toggleSelectFacetValue({
Expand All @@ -155,14 +190,26 @@ describe('headless breadcrumb manager', () => {
})
);
});

it('dispatches a toggleExcludeFacetValue action when #deselectBreadcrumb is called for an excluded facet value', () => {
breadcrumbManager.deselectBreadcrumb(facetBreadcrumbs[0].values[1]);
expect(engine.actions).toContainEqual(
toggleExcludeFacetValue({
facetId,
selection: mockExcludedValue,
})
);
});
});

describe('date facet breadcrumbs', () => {
let mockSelectedValue: DateFacetValue;
let mockExcludedValue: DateFacetValue;
let facetBreadcrumbs: DateFacetBreadcrumb[];

beforeEach(() => {
mockSelectedValue = buildMockDateFacetValue({state: 'selected'});
mockExcludedValue = buildMockDateFacetValue({state: 'excluded'});

state = createMockState({
search: {
Expand All @@ -172,7 +219,7 @@ describe('headless breadcrumb manager', () => {
facets: [
buildMockDateFacetResponse({
facetId,
values: [mockSelectedValue],
values: [mockSelectedValue, mockExcludedValue],
}),
],
},
Expand All @@ -190,6 +237,7 @@ describe('headless breadcrumb manager', () => {

it('#state gets date facet breadcrumbs correctly', () => {
expect(facetBreadcrumbs[0].values[0].value).toBe(mockSelectedValue);
expect(facetBreadcrumbs[0].values[1].value).toBe(mockExcludedValue);
});

it('dispatches an executeSearch action on selection', () => {
Expand All @@ -207,7 +255,7 @@ describe('headless breadcrumb manager', () => {
);
});

it('dispatches a toggleSelectDateFacetValue action when #deselectBreadcrumb is called', () => {
it('dispatches a toggleSelectDateFacetValue action when #deselectBreadcrumb is called for a selected facet value', () => {
breadcrumbManager.deselectBreadcrumb(facetBreadcrumbs[0].values[0]);
expect(engine.actions).toContainEqual(
toggleSelectDateFacetValue({
Expand All @@ -216,14 +264,26 @@ describe('headless breadcrumb manager', () => {
})
);
});

it('dispatches a toggleExcludeDateFacetValue action when #deselectBreadcrumb is called for an excluded facet value', () => {
breadcrumbManager.deselectBreadcrumb(facetBreadcrumbs[0].values[1]);
expect(engine.actions).toContainEqual(
toggleExcludeDateFacetValue({
facetId,
selection: mockExcludedValue,
})
);
});
});

describe('numeric facet breadcrumbs', () => {
let mockSelectedValue: NumericFacetValue;
let mockExcludedValue: NumericFacetValue;
let facetBreadcrumbs: NumericFacetBreadcrumb[];

beforeEach(() => {
mockSelectedValue = buildMockNumericFacetValue({state: 'selected'});
mockExcludedValue = buildMockNumericFacetValue({state: 'excluded'});

state = createMockState({
search: {
Expand All @@ -233,7 +293,7 @@ describe('headless breadcrumb manager', () => {
facets: [
buildMockNumericFacetResponse({
facetId,
values: [mockSelectedValue],
values: [mockSelectedValue, mockExcludedValue],
}),
],
},
Expand All @@ -251,24 +311,30 @@ describe('headless breadcrumb manager', () => {

it('#state gets numeric facet breadcrumbs correctly', () => {
expect(facetBreadcrumbs[0].values[0].value).toBe(mockSelectedValue);
expect(facetBreadcrumbs[0].values[1].value).toBe(mockExcludedValue);
});

it('dispatches an executeSearch action on selection', () => {
facetBreadcrumbs[0].values[0].deselect();
expect(engine.findAsyncAction(executeSearch.pending)).toBeTruthy();
});

it('dispatches a toggleSelectNumericFacetValue action on selection', () => {
facetBreadcrumbs[0].values[0].deselect();
it('dispatches an executeSearch action on exclusion', () => {
facetBreadcrumbs[0].values[1].deselect();
expect(engine.findAsyncAction(executeSearch.pending)).toBeTruthy();
});

it('dispatches a toggleExcludeNumericFacetValue action on exclusion', () => {
facetBreadcrumbs[0].values[1].deselect();
expect(engine.actions).toContainEqual(
toggleSelectNumericFacetValue({
toggleExcludeNumericFacetValue({
facetId,
selection: mockSelectedValue,
selection: mockExcludedValue,
})
);
});

it('dispatches a toggleSelectNumericFacetValue action when #deselectBreadcrumb is called', () => {
it('dispatches a toggleSelectNumericFacetValue action when #deselectBreadcrumb is called for a selected facet value', () => {
breadcrumbManager.deselectBreadcrumb(facetBreadcrumbs[0].values[0]);
expect(engine.actions).toContainEqual(
toggleSelectNumericFacetValue({
Expand All @@ -277,6 +343,15 @@ describe('headless breadcrumb manager', () => {
})
);
});
it('dispatches a toggleExcludeNumericFacetValue action when #deselectBreadcrumb is called for an excluded facet value', () => {
breadcrumbManager.deselectBreadcrumb(facetBreadcrumbs[0].values[1]);
expect(engine.actions).toContainEqual(
toggleExcludeNumericFacetValue({
facetId,
selection: mockExcludedValue,
})
);
});
});

describe('category facet breadcrumbs', () => {
Expand Down Expand Up @@ -339,7 +414,7 @@ describe('headless breadcrumb manager', () => {
);
});

it('dispatches a deselectAllCategoryFacetValues action when #deselectBreadcrumb is called', () => {
it('dispatches a deselectAllCategoryFacetValues action when #deselectBreadcrumb is called for a selected facet value', () => {
breadcrumbManager.deselectBreadcrumb(facetBreadcrumbs[0]);
expect(engine.actions).toContainEqual(
deselectAllCategoryFacetValues(facetId)
Expand All @@ -354,10 +429,17 @@ describe('headless breadcrumb manager', () => {
caption: 'c',
state: 'selected',
});
const excluded = buildMockStaticFilterValue({
caption: 'd',
state: 'excluded',
});

beforeEach(() => {
state.staticFilterSet = {
[id]: buildMockStaticFilterSlice({id, values: [idle, selected]}),
[id]: buildMockStaticFilterSlice({
id,
values: [idle, selected, excluded],
}),
};
});

Expand All @@ -369,33 +451,57 @@ describe('headless breadcrumb manager', () => {
expect(firstFilter.id).toBe(id);

const {values} = firstFilter;
expect(values.length).toBe(1);
expect(values.length).toBe(2);

expect(values[0].value.caption).toBe(selected.caption);
expect(values[1].value.caption).toBe(excluded.caption);
});

it('#state.hasBreadcrumbs returns true', () => {
expect(breadcrumbManager.state.hasBreadcrumbs).toBe(true);
});

describe('#deselectBreadcrumb with a static filter breadcrumb value dispatches the correct actions', () => {
beforeEach(() => {
const {staticFilterBreadcrumbs} = breadcrumbManager.state;
const [firstBreadcrumb] = staticFilterBreadcrumbs[0].values;
describe('#selected values', () => {
beforeEach(() => {
const {staticFilterBreadcrumbs} = breadcrumbManager.state;
const [selectedBreadcrumb] = staticFilterBreadcrumbs[0].values;
breadcrumbManager.deselectBreadcrumb(selectedBreadcrumb);
});

breadcrumbManager.deselectBreadcrumb(firstBreadcrumb);
});
it('dispatches #toggleSelectStaticFilterValue', () => {
const toggleSelect = toggleSelectStaticFilterValue({
id,
value: selected,
});
expect(engine.actions).toContainEqual(toggleSelect);
});

it('dispatches #toggleSelectStaticFilterValue', () => {
const toggleSelect = toggleSelectStaticFilterValue({
id,
value: selected,
it('dispatches #executeSearch', () => {
const action = engine.findAsyncAction(executeSearch.pending);
expect(action).toBeTruthy();
});
expect(engine.actions).toContainEqual(toggleSelect);
});

it('dispatches #executeSearch', () => {
const action = engine.findAsyncAction(executeSearch.pending);
expect(action).toBeTruthy();
describe('#excluded values', () => {
beforeEach(() => {
const {staticFilterBreadcrumbs} = breadcrumbManager.state;
const [, excludedBreadcrumb] = staticFilterBreadcrumbs[0].values;
breadcrumbManager.deselectBreadcrumb(excludedBreadcrumb);
});

it('dispatches #toggleExcludeStaticFilterValue', () => {
const toggleExclude = toggleExcludeStaticFilterValue({
id,
value: excluded,
});
expect(engine.actions).toContainEqual(toggleExclude);
});

it('dispatches #executeSearch', () => {
const action = engine.findAsyncAction(executeSearch.pending);
expect(action).toBeTruthy();
});
});
});
});
Expand All @@ -411,7 +517,18 @@ describe('headless breadcrumb manager', () => {
expect(breadcrumbManager.state.hasBreadcrumbs).toBe(true);
});

it('hasBreadcrumbs returns false when no facet value is selected', () => {
it('hasBreadcrumbs returns true when a facet value is excluded', () => {
state.numericFacetSet[facetId] = buildMockNumericFacetSlice({
request: buildMockNumericFacetRequest({facetId}),
});
const mockSelectedValue = buildMockNumericFacetValue({state: 'excluded'});
state.search.response.facets = [
buildMockNumericFacetResponse({facetId, values: [mockSelectedValue]}),
];
expect(breadcrumbManager.state.hasBreadcrumbs).toBe(true);
});

it('hasBreadcrumbs returns false when no facet value is selected or excluded', () => {
state.search.response.facets = [];
expect(breadcrumbManager.state.hasBreadcrumbs).toBe(false);
});
Expand Down
Loading

0 comments on commit 21d8259

Please sign in to comment.