diff --git a/src/connectors/clear-refinements/connectClearRefinements.ts b/src/connectors/clear-refinements/connectClearRefinements.ts index bcfc0c191d..c5e211a149 100644 --- a/src/connectors/clear-refinements/connectClearRefinements.ts +++ b/src/connectors/clear-refinements/connectClearRefinements.ts @@ -195,7 +195,7 @@ function getAttributesToClear({ excludedAttributes, transformItems, }): AttributesToClear { - const clearsQuery = + const includesQuery = includedAttributes.indexOf('query') !== -1 || excludedAttributes.indexOf('query') === -1; @@ -206,7 +206,7 @@ function getAttributesToClear({ getRefinements( scopedResult.results, scopedResult.helper.state, - clearsQuery + includesQuery ) .map(refinement => refinement.attribute) .filter( @@ -219,7 +219,7 @@ function getAttributesToClear({ .filter( attribute => // If the query is included, we ignore the default `excludedAttributes = ['query']` - (attribute === 'query' && clearsQuery) || + (attribute === 'query' && includesQuery) || // Otherwise, ignore the excluded attributes excludedAttributes.indexOf(attribute) === -1 ) diff --git a/src/connectors/current-refinements/connectCurrentRefinements.ts b/src/connectors/current-refinements/connectCurrentRefinements.ts index dda594b7cc..4a21a97a88 100644 --- a/src/connectors/current-refinements/connectCurrentRefinements.ts +++ b/src/connectors/current-refinements/connectCurrentRefinements.ts @@ -249,7 +249,7 @@ function getRefinementsItems({ includedAttributes: CurrentRefinementsConnectorParams['includedAttributes']; excludedAttributes: CurrentRefinementsConnectorParams['excludedAttributes']; }): CurrentRefinementsConnectorParamsItem[] { - const clearsQuery = + const includesQuery = (includedAttributes || []).indexOf('query') !== -1 || (excludedAttributes || []).indexOf('query') === -1; @@ -259,7 +259,7 @@ function getRefinementsItems({ : (item: CurrentRefinementsConnectorParamsRefinement) => excludedAttributes!.indexOf(item.attribute) === -1; - const items = getRefinements(results, helper.state, clearsQuery) + const items = getRefinements(results, helper.state, includesQuery) .map(normalizeRefinement) .filter(filterFunction); diff --git a/src/connectors/query-rules/__tests__/connectQueryRules-test.ts b/src/connectors/query-rules/__tests__/connectQueryRules-test.ts index 5cf1c642a8..9c3e2fc607 100644 --- a/src/connectors/query-rules/__tests__/connectQueryRules-test.ts +++ b/src/connectors/query-rules/__tests__/connectQueryRules-test.ts @@ -559,7 +559,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/query-rules expect(priceFilterSpy).toHaveBeenCalledWith([500, 400, 100]); }); - test('can filter trackedFilters with facets refinements', () => { + test('can track filters from facets refinements', () => { const helper = createFakeHelper({ disjunctiveFacets: ['brand'], }); @@ -637,7 +637,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/query-rules expect(brandFilterSpy).toHaveBeenCalledWith(['Samsung', 'Apple']); }); - test('can filter tracked filters from numeric refinements', () => { + test('can track filters from numeric refinements', () => { const helper = createFakeHelper(); const priceFilterSpy = jest.fn(() => [500]); const { makeWidget } = createWidget(); @@ -702,6 +702,88 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/query-rules expect(priceFilterSpy).toHaveBeenCalledWith([500, 400, 100]); }); + test('can track filters from query', () => { + const helper = createFakeHelper(); + const querySpy = jest.fn(filters => { + const [query] = filters as string[]; + return query.includes('cat') ? [query] : []; + }); + const { makeWidget } = createWidget(); + const widget = makeWidget({ + trackedFilters: { + query: querySpy, + }, + }); + + widget.init!( + createInitOptions({ + helper, + state: helper.state, + }) + ); + + expect((helper.state as SearchParameters).ruleContexts).toEqual( + undefined + ); + expect(querySpy).toHaveBeenCalledTimes(0); + + widget.render!( + createRenderOptions({ + helper, + state: helper.state, + results: new SearchResults(helper.state, [ + createSingleSearchResponse(), + createSingleSearchResponse(), + ]), + }) + ); + + expect((helper.state as SearchParameters).ruleContexts).toEqual( + undefined + ); + expect(querySpy).toHaveBeenCalledTimes(0); + + helper.setState({ + query: 'cats are cool', + }); + + widget.render!( + createRenderOptions({ + helper, + state: helper.state, + results: new SearchResults(helper.state, [ + createSingleSearchResponse(), + createSingleSearchResponse(), + ]), + }) + ); + + expect((helper.state as SearchParameters).ruleContexts).toEqual([ + 'ais-query-cats_are_cool', + ]); + expect(querySpy).toHaveBeenCalledTimes(1); + expect(querySpy).toHaveBeenCalledWith(['cats are cool']); + + helper.setState({ + query: 'dogs are cool', + }); + + widget.render!( + createRenderOptions({ + helper, + state: helper.state, + results: new SearchResults(helper.state, [ + createSingleSearchResponse(), + createSingleSearchResponse(), + ]), + }) + ); + + expect((helper.state as SearchParameters).ruleContexts).toBeUndefined(); + expect(querySpy).toHaveBeenCalledTimes(2); + expect(querySpy).toHaveBeenCalledWith(['dogs are cool']); + }); + test('escapes all ruleContexts before passing them to search parameters', () => { const helper = createFakeHelper({ disjunctiveFacets: ['brand'], diff --git a/src/connectors/query-rules/connectQueryRules.ts b/src/connectors/query-rules/connectQueryRules.ts index fa1cbd45e7..d5f73399b2 100644 --- a/src/connectors/query-rules/connectQueryRules.ts +++ b/src/connectors/query-rules/connectQueryRules.ts @@ -74,7 +74,8 @@ function getRuleContextsFromTrackedFilters({ // An empty object is technically not a `SearchResults` but `getRefinements` // only accesses properties, meaning it will not throw with an empty object. helper.lastResults || ({} as SearchResults), - sharedHelperState + sharedHelperState, + true ) .filter( (refinement: InternalRefinement) => refinement.attribute === facetName diff --git a/src/lib/utils/getRefinements.ts b/src/lib/utils/getRefinements.ts index 0d79a903bc..293f6bada5 100644 --- a/src/lib/utils/getRefinements.ts +++ b/src/lib/utils/getRefinements.ts @@ -93,7 +93,7 @@ function getRefinement( function getRefinements( results: SearchResults, state: SearchParameters, - clearsQuery: boolean = false + includesQuery: boolean = false ): Refinement[] { const refinements: Refinement[] = []; const { @@ -188,7 +188,7 @@ function getRefinements( refinements.push({ type: 'tag', attribute: '_tags', name: refinementName }); }); - if (clearsQuery && state.query && state.query.trim()) { + if (includesQuery && state.query && state.query.trim()) { refinements.push({ attribute: 'query', type: 'query',