From acece2b7b6c0c463942d1b1c2073ec79f925ea37 Mon Sep 17 00:00:00 2001 From: EmilyZhang777 <48967088+EmilyZhang777@users.noreply.github.com> Date: Fri, 26 Jan 2024 09:06:26 -0600 Subject: [PATCH 1/5] Remove real live api key (#164) (#165) Merge develop into main --- test-site-node/index.js | 2 +- test-site-node/package-lock.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test-site-node/index.js b/test-site-node/index.js index 3b36be4f..5a57a2ea 100644 --- a/test-site-node/index.js +++ b/test-site-node/index.js @@ -1,7 +1,7 @@ const { provideHeadless } = require('@yext/search-headless'); const answers = provideHeadless({ - apiKey: '2d8c550071a64ea23e263118a2b0680b', + apiKey: process.env.API_KEY, experienceKey: 'slanswers', locale: 'en' }); diff --git a/test-site-node/package-lock.json b/test-site-node/package-lock.json index 89ba2b56..f3cff820 100644 --- a/test-site-node/package-lock.json +++ b/test-site-node/package-lock.json @@ -14,11 +14,11 @@ }, "..": { "name": "@yext/search-headless", - "version": "2.5.0-alpha.2", + "version": "2.5.0", "license": "BSD-3-Clause", "dependencies": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "^2.5.0-alpha.2", + "@yext/search-core": "^2.5.0", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" }, @@ -65,7 +65,7 @@ "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.4.0", "@yext/eslint-plugin-export-star": "^1.0.0", - "@yext/search-core": "^2.5.0-alpha.2", + "@yext/search-core": "^2.5.0", "babel-jest": "^27.4.5", "eslint": "^8.11.0", "eslint-config-react-app": "^7.0.1", From 70664ab0b29445c5d293589c4086ace93d76fcb0 Mon Sep 17 00:00:00 2001 From: Fondryext <160865254+Fondryext@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:36:44 -0400 Subject: [PATCH 2/5] Add isPagination variable to QueryState, and use for reusing queryId (#175) This change adds a new param to QueryState, which is used inside of vertical searches to reuse the previous queryId, if any. It is then set to false, requiring callers to specify every time that the next search is a pagination. J=WAT-4105 TEST=auto wrote new tests --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- ...search-headless.querystate.ispagination.md | 13 ++++++++++ docs/search-headless.querystate.md | 1 + docs/search-headless.searchheadless.md | 1 + ...headless.searchheadless.setispagination.md | 24 ++++++++++++++++++ etc/search-headless.api.md | 2 ++ package-lock.json | 4 +-- package.json | 2 +- src/models/slices/query.ts | 8 ++++-- src/search-headless.ts | 14 ++++++++++- src/slices/query.ts | 3 +++ tests/integration/verticalsearch.ts | 25 +++++++++++++++++-- tests/unit/slices/query.ts | 12 +++++++-- 12 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 docs/search-headless.querystate.ispagination.md create mode 100644 docs/search-headless.searchheadless.setispagination.md diff --git a/docs/search-headless.querystate.ispagination.md b/docs/search-headless.querystate.ispagination.md new file mode 100644 index 00000000..bb32a332 --- /dev/null +++ b/docs/search-headless.querystate.ispagination.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-headless](./search-headless.md) > [QueryState](./search-headless.querystate.md) > [isPagination](./search-headless.querystate.ispagination.md) + +## QueryState.isPagination property + +Whether the next query represents a pagination - in which case queryId will be maintained + +Signature: + +```typescript +isPagination?: boolean; +``` diff --git a/docs/search-headless.querystate.md b/docs/search-headless.querystate.md index 78cde84a..3bd35e32 100644 --- a/docs/search-headless.querystate.md +++ b/docs/search-headless.querystate.md @@ -17,6 +17,7 @@ export interface QueryState | Property | Type | Description | | --- | --- | --- | | [input?](./search-headless.querystate.input.md) | string | (Optional) The user input used for the next search query. | +| [isPagination?](./search-headless.querystate.ispagination.md) | boolean | (Optional) Whether the next query represents a pagination - in which case queryId will be maintained | | [mostRecentSearch?](./search-headless.querystate.mostrecentsearch.md) | string | (Optional) The query of the most recent search. | | [queryId?](./search-headless.querystate.queryid.md) | string | (Optional) The ID of the query from the latest search. | | [querySource?](./search-headless.querystate.querysource.md) | [QuerySource](./search-headless.querysource.md) | (Optional) The source of the query (from a standard Search integration, a Search overlay, or from visual autocomplete). | diff --git a/docs/search-headless.searchheadless.md b/docs/search-headless.searchheadless.md index 4eb2e96e..4440b079 100644 --- a/docs/search-headless.searchheadless.md +++ b/docs/search-headless.searchheadless.md @@ -41,6 +41,7 @@ export default class SearchHeadless | [setFacetOption(fieldId, facetOption, selected)](./search-headless.searchheadless.setfacetoption.md) | | Sets a specified facet option to be selected or unselected. | | [setFacets(facets)](./search-headless.searchheadless.setfacets.md) | | Sets [FiltersState.facets](./search-headless.filtersstate.facets.md) to the specified facets. | | [setFilterOption(filter)](./search-headless.searchheadless.setfilteroption.md) | | Sets a static filter option and whether or not it is selected in state. | +| [setIsPagination(input)](./search-headless.searchheadless.setispagination.md) | | Sets [QueryState.isPagination](./search-headless.querystate.ispagination.md) to the specified input. | | [setLocationRadius(locationRadius)](./search-headless.searchheadless.setlocationradius.md) | | Sets [VerticalSearchState.locationRadius](./search-headless.verticalsearchstate.locationradius.md) to the specified number of meters. | | [setOffset(offset)](./search-headless.searchheadless.setoffset.md) | | Sets [VerticalSearchState.offset](./search-headless.verticalsearchstate.offset.md) to the specified offset. | | [setQuery(input)](./search-headless.searchheadless.setquery.md) | | Sets [QueryState.input](./search-headless.querystate.input.md) to the specified input. | diff --git a/docs/search-headless.searchheadless.setispagination.md b/docs/search-headless.searchheadless.setispagination.md new file mode 100644 index 00000000..9a071bd7 --- /dev/null +++ b/docs/search-headless.searchheadless.setispagination.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@yext/search-headless](./search-headless.md) > [SearchHeadless](./search-headless.searchheadless.md) > [setIsPagination](./search-headless.searchheadless.setispagination.md) + +## SearchHeadless.setIsPagination() method + +Sets [QueryState.isPagination](./search-headless.querystate.ispagination.md) to the specified input. + +Signature: + +```typescript +setIsPagination(input: boolean): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | boolean | The input to set | + +Returns: + +void + diff --git a/etc/search-headless.api.md b/etc/search-headless.api.md index da54ff0e..e2fe6270 100644 --- a/etc/search-headless.api.md +++ b/etc/search-headless.api.md @@ -654,6 +654,7 @@ export enum QuerySource { // @public export interface QueryState { input?: string; + isPagination?: boolean; mostRecentSearch?: string; queryId?: string; querySource?: QuerySource; @@ -780,6 +781,7 @@ export class SearchHeadless { setFacetOption(fieldId: string, facetOption: FacetOption, selected: boolean): void; setFacets(facets: DisplayableFacet[]): void; setFilterOption(filter: SelectableStaticFilter): void; + setIsPagination(input: boolean): void; setLocationRadius(locationRadius: number | undefined): void; setOffset(offset: number): void; setQuery(input: string): void; diff --git a/package-lock.json b/package-lock.json index 2da3ed37..1ec8cc0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@yext/search-headless", - "version": "2.5.0", + "version": "2.5.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@yext/search-headless", - "version": "2.5.0", + "version": "2.5.1", "license": "BSD-3-Clause", "dependencies": { "@reduxjs/toolkit": "^1.8.1", diff --git a/package.json b/package.json index ab66c199..c5718222 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-headless", - "version": "2.5.0", + "version": "2.5.1", "description": "A library for powering UI components for Yext Search integrations", "author": "slapshot@yext.com", "license": "BSD-3-Clause", diff --git a/src/models/slices/query.ts b/src/models/slices/query.ts index d1141717..760faab1 100644 --- a/src/models/slices/query.ts +++ b/src/models/slices/query.ts @@ -30,5 +30,9 @@ export interface QueryState { /** * The computed intents of the mostRecentSearch, as returned by the Search API. */ - searchIntents?: SearchIntent[] -} \ No newline at end of file + searchIntents?: SearchIntent[], + /** + * Whether the next query represents a pagination - in which case queryId will be maintained + */ + isPagination?: boolean +} diff --git a/src/search-headless.ts b/src/search-headless.ts index 6d981348..3af535cb 100644 --- a/src/search-headless.ts +++ b/src/search-headless.ts @@ -55,6 +55,15 @@ export default class SearchHeadless { private additionalHttpHeaders?: AdditionalHttpHeaders ) {} + /** + * Sets {@link QueryState.isPagination} to the specified input. + * + * @param input - The input to set + */ + setIsPagination(input: boolean): void { + this.stateManager.dispatchEvent('query/setIsPagination', input); + } + /** * Sets {@link QueryState.input} to the specified input. * @@ -402,7 +411,7 @@ export default class SearchHeadless { return; } this.stateManager.dispatchEvent('searchStatus/setIsLoading', true); - const { input, querySource, queryTrigger } = this.state.query; + const { input, isPagination, queryId, querySource, queryTrigger } = this.state.query; const skipSpellCheck = !this.state.spellCheck.enabled; const sessionTrackingEnabled = this.state.sessionTracking.enabled; const sessionId = this.state.sessionTracking.sessionId; @@ -411,6 +420,7 @@ export default class SearchHeadless { const { limit, offset, sortBys, locationRadius } = this.state.vertical; const { referrerPageUrl, context } = this.state.meta; const { userLocation } = this.state.location; + const nextQueryId = isPagination ? queryId : undefined; const facetsToApply = facets?.map(facet => { return { @@ -437,6 +447,7 @@ export default class SearchHeadless { context, referrerPageUrl, locationRadius, + queryId: nextQueryId, additionalHttpHeaders: this.additionalHttpHeaders }; @@ -457,6 +468,7 @@ export default class SearchHeadless { } this.stateManager.dispatchEvent('query/setQueryId', response.queryId); this.stateManager.dispatchEvent('query/setMostRecentSearch', input); + this.stateManager.dispatchEvent('query/setIsPagination', false); this.stateManager.dispatchEvent('filters/setFacets', response.facets); this.stateManager.dispatchEvent('spellCheck/setResult', response.spellCheck); this.stateManager.dispatchEvent('query/setSearchIntents', response.searchIntents || []); diff --git a/src/slices/query.ts b/src/slices/query.ts index 84fb2f4f..cc500785 100644 --- a/src/slices/query.ts +++ b/src/slices/query.ts @@ -8,6 +8,9 @@ const reducers = { setInput: (state, action: PayloadAction) => { state.input = action.payload; }, + setIsPagination: (state, action: PayloadAction) => { + state.isPagination = action.payload; + }, setTrigger: (state, action: PayloadAction) => { state.queryTrigger = action.payload; }, diff --git a/tests/integration/verticalsearch.ts b/tests/integration/verticalsearch.ts index bddabaa6..cb3a92ae 100644 --- a/tests/integration/verticalsearch.ts +++ b/tests/integration/verticalsearch.ts @@ -169,6 +169,23 @@ it('answers.setVerticalLimit sets the vertical limit when a number is passed to expect(answers.state.vertical.limit).toEqual(7); }); +it('vertical searches re-use queryId when isPagination is true', async () => { + const answers = createMockedHeadless( { verticalSearch: createMockSearch() }, initialState); + await answers.executeVerticalQuery(); + const firstQueryId = answers.state.query.queryId; + answers.setIsPagination(true); + await answers.executeVerticalQuery(); + expect(answers.state.query.queryId).toEqual(firstQueryId); +}); + +it('vertical searches change queryId when isPagination is not true', async () => { + const answers = createMockedHeadless( { verticalSearch: createMockSearch() }, initialState); + await answers.executeVerticalQuery(); + const firstQueryId = answers.state.query.queryId; + await answers.executeVerticalQuery(); + expect(answers.state.query.queryId).not.toEqual(firstQueryId); +}); + it('handle a rejected promise from core', async () => { const mockSearch = createMockRejectedSearch(); const mockCore = { verticalSearch: mockSearch }; @@ -199,7 +216,11 @@ it('executeVerticalQuery passes the additional HTTP headers', async () => { function createMockSearch() { return jest.fn(async (_request: VerticalSearchRequest) => { await setTimeout(0); - return Promise.resolve({}); + return Promise.resolve({ + // preserves queryId if passed-in, else generates a random string + queryId: _request.queryId == undefined ? + (Math.random()).toString(36).substring(2) : _request.queryId + }); }); } @@ -208,4 +229,4 @@ function createMockRejectedSearch() { await setTimeout(0); return Promise.reject('mock error message'); }); -} \ No newline at end of file +} diff --git a/tests/unit/slices/query.ts b/tests/unit/slices/query.ts index 97c2966b..622d6923 100644 --- a/tests/unit/slices/query.ts +++ b/tests/unit/slices/query.ts @@ -2,7 +2,7 @@ import { QuerySource, QueryTrigger } from '@yext/search-core'; import createQuerySlice from '../../../src/slices/query'; const { reducer, actions } = createQuerySlice(''); -const { setInput, setQueryId, setSource, setTrigger } = actions; +const { setInput, setIsPagination, setQueryId, setSource, setTrigger } = actions; describe('query slice reducer works as expected', () => { it('setQuery action is handled properly', () => { @@ -13,6 +13,14 @@ describe('query slice reducer works as expected', () => { expect(actualState).toEqual(expectedState); }); + it('setIsPagination action is handled properly', () => { + const isPagination = true; + const expectedState = { isPagination }; + const actualState = reducer({}, setIsPagination(isPagination)); + + expect(actualState).toEqual(expectedState); + }); + it('setTrigger action is handled properly', () => { const queryTrigger = QueryTrigger.Initialize; const expectedState = { queryTrigger }; @@ -36,4 +44,4 @@ describe('query slice reducer works as expected', () => { expect(actualState).toEqual(expectedState); }); -}); \ No newline at end of file +}); From a6fac92c3d43be7da591b6ef503211a8534c9901 Mon Sep 17 00:00:00 2001 From: Fondryext <160865254+Fondryext@users.noreply.github.com> Date: Wed, 10 Jul 2024 09:20:53 -0400 Subject: [PATCH 3/5] Update package-lock.json add missing comma --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 366c68a0..189d2317 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@yext/search-headless", - "version": "2.5.1" + "version": "2.5.1", "license": "BSD-3-Clause", "dependencies": { "@reduxjs/toolkit": "^1.8.1", From 37d4a05ceaa59ef7940bba4b06a492de2f7307a9 Mon Sep 17 00:00:00 2001 From: Fondryext <160865254+Fondryext@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:02:01 -0400 Subject: [PATCH 4/5] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8370b0dc..adde8fd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-headless", - "version": "2.5.1", + "version": "2.6.0-beta.4", "description": "A library for powering UI components for Yext Search integrations", "author": "slapshot@yext.com", "license": "BSD-3-Clause", From f2bbfe75a4ae55c988ee768b7cda6e32de79375d Mon Sep 17 00:00:00 2001 From: Fondryext <160865254+Fondryext@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:02:24 -0400 Subject: [PATCH 5/5] Update package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 189d2317..35c5946a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@yext/search-headless", - "version": "2.5.1", + "version": "2.6.0-beta.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@yext/search-headless", - "version": "2.5.1", + "version": "2.6.0-beta.4", "license": "BSD-3-Clause", "dependencies": { "@reduxjs/toolkit": "^1.8.1",