Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge main (v2.5.1) into develop #177

Merged
merged 7 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/search-headless.querystate.ispagination.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/search-headless](./search-headless.md) &gt; [QueryState](./search-headless.querystate.md) &gt; [isPagination](./search-headless.querystate.ispagination.md)

## QueryState.isPagination property

Whether the next query represents a pagination - in which case queryId will be maintained

<b>Signature:</b>

```typescript
isPagination?: boolean;
```
1 change: 1 addition & 0 deletions docs/search-headless.querystate.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface QueryState
| Property | Type | Description |
| --- | --- | --- |
| [input?](./search-headless.querystate.input.md) | string | <i>(Optional)</i> The user input used for the next search query. |
| [isPagination?](./search-headless.querystate.ispagination.md) | boolean | <i>(Optional)</i> Whether the next query represents a pagination - in which case queryId will be maintained |
| [mostRecentSearch?](./search-headless.querystate.mostrecentsearch.md) | string | <i>(Optional)</i> The query of the most recent search. |
| [queryId?](./search-headless.querystate.queryid.md) | string | <i>(Optional)</i> The ID of the query from the latest search. |
| [querySource?](./search-headless.querystate.querysource.md) | [QuerySource](./search-headless.querysource.md) | <i>(Optional)</i> The source of the query (from a standard Search integration, a Search overlay, or from visual autocomplete). |
Expand Down
1 change: 1 addition & 0 deletions docs/search-headless.searchheadless.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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. |
Expand Down
24 changes: 24 additions & 0 deletions docs/search-headless.searchheadless.setispagination.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/search-headless](./search-headless.md) &gt; [SearchHeadless](./search-headless.searchheadless.md) &gt; [setIsPagination](./search-headless.searchheadless.setispagination.md)

## SearchHeadless.setIsPagination() method

Sets [QueryState.isPagination](./search-headless.querystate.ispagination.md) to the specified input.

<b>Signature:</b>

```typescript
setIsPagination(input: boolean): void;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| input | boolean | The input to set |

<b>Returns:</b>

void

2 changes: 2 additions & 0 deletions etc/search-headless.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ export enum QuerySource {
// @public
export interface QueryState {
input?: string;
isPagination?: boolean;
mostRecentSearch?: string;
queryId?: string;
querySource?: QuerySource;
Expand Down Expand Up @@ -809,6 +810,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;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yext/search-headless",
"version": "2.6.0-beta.3",
"version": "2.6.0-beta.4",
"description": "A library for powering UI components for Yext Search integrations",
"author": "[email protected]",
"license": "BSD-3-Clause",
Expand Down
8 changes: 6 additions & 2 deletions src/models/slices/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,9 @@ export interface QueryState {
/**
* The computed intents of the mostRecentSearch, as returned by the Search API.
*/
searchIntents?: SearchIntent[]
}
searchIntents?: SearchIntent[],
/**
* Whether the next query represents a pagination - in which case queryId will be maintained
*/
isPagination?: boolean
}
14 changes: 13 additions & 1 deletion src/search-headless.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,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.
*
Expand Down Expand Up @@ -406,7 +415,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;
Expand All @@ -415,6 +424,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 {
Expand All @@ -441,6 +451,7 @@ export default class SearchHeadless {
context,
referrerPageUrl,
locationRadius,
queryId: nextQueryId,
additionalHttpHeaders: this.additionalHttpHeaders
};

Expand All @@ -461,6 +472,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 || []);
Expand Down
3 changes: 3 additions & 0 deletions src/slices/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const reducers = {
setInput: (state, action: PayloadAction<string>) => {
state.input = action.payload;
},
setIsPagination: (state, action: PayloadAction<boolean>) => {
state.isPagination = action.payload;
},
setTrigger: (state, action: PayloadAction<QueryTrigger>) => {
state.queryTrigger = action.payload;
},
Expand Down
25 changes: 23 additions & 2 deletions tests/integration/verticalsearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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
});
});
}

Expand All @@ -208,4 +229,4 @@ function createMockRejectedSearch() {
await setTimeout(0);
return Promise.reject('mock error message');
});
}
}
12 changes: 10 additions & 2 deletions tests/unit/slices/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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 };
Expand All @@ -36,4 +44,4 @@ describe('query slice reducer works as expected', () => {

expect(actualState).toEqual(expectedState);
});
});
});
Loading