From e620701cb7d528ea4d3fe823bd0104e1bdd73a31 Mon Sep 17 00:00:00 2001 From: Dennis Konieczek Date: Tue, 3 Oct 2023 13:16:40 -0400 Subject: [PATCH 1/2] feat: add autocomplete settings.redirects.singleResult config --- .../AutocompleteController.test.ts | 51 +++++++++++++++ .../Autocomplete/AutocompleteController.ts | 10 +++ .../autocomplete/8uyt2m/singleResult.json | 65 +++++++++++++++++++ packages/snap-store-mobx/src/types.ts | 24 +++++++ 4 files changed, 150 insertions(+) create mode 100644 packages/snap-shared/src/MockData/autocomplete/8uyt2m/singleResult.json diff --git a/packages/snap-controller/src/Autocomplete/AutocompleteController.test.ts b/packages/snap-controller/src/Autocomplete/AutocompleteController.test.ts index 3febd0a46..b587c067b 100644 --- a/packages/snap-controller/src/Autocomplete/AutocompleteController.test.ts +++ b/packages/snap-controller/src/Autocomplete/AutocompleteController.test.ts @@ -841,6 +841,57 @@ describe('Autocomplete Controller', () => { }); }); + it('can redirect url when singleResult', async () => { + document.body.innerHTML = '
'; + acConfig = { + ...acConfig, + selector: '#search_query', + action: '/search', + settings: { + redirects: { + singleResult: true, + }, + }, + }; + + const controller = new AutocompleteController(acConfig, { + client: new MockClient(globals, {}), + store: new AutocompleteStore(acConfig, services), + urlManager, + eventManager: new EventManager(), + profiler: new Profiler(), + logger: new Logger(), + tracker: new Tracker(globals), + }); + (controller.client as MockClient).mockData.updateConfig({ autocomplete: 'singleResult', siteId: '8uyt2m' }); + + const query = 'dress'; + controller.urlManager = controller.urlManager.set('query', query); + + await controller.bind(); + const inputEl: HTMLInputElement = document.querySelector(controller.config.selector)!; + expect(inputEl).toBeDefined(); + + inputEl.value = query; + + await controller.search(); + expect(controller.store.results.length).toBe(1); + + // @ts-ignore + delete window.location; + window.location = { + ...window.location, + href: '', // jest does not support window location changes + }; + + inputEl.focus(); + inputEl.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, keyCode: KEY_ENTER })); + + await waitFor(() => { + expect(window.location.href).toContain(controller.store.results[0].mappings.core!.url); + }); + }); + it('tests bind method without form (using config.action)', async () => { acConfig = { ...acConfig, diff --git a/packages/snap-controller/src/Autocomplete/AutocompleteController.ts b/packages/snap-controller/src/Autocomplete/AutocompleteController.ts index cdea10cc8..394b3b03a 100644 --- a/packages/snap-controller/src/Autocomplete/AutocompleteController.ts +++ b/packages/snap-controller/src/Autocomplete/AutocompleteController.ts @@ -33,6 +33,7 @@ const defaultConfig: AutocompleteControllerConfig = { }, redirects: { merchandising: true, + singleResult: true, }, }, }; @@ -108,6 +109,15 @@ export class AutocompleteController extends AbstractController { window.location.href = redirectURL; return false; } + + if (this.config?.settings?.redirects?.singleResult) { + const { results } = (ac.controller as AutocompleteController).store; + const singleResultUrl = results.length === 1 && results[0].type === 'product' && results[0].mappings.core?.url; + if (singleResultUrl) { + window.location.href = singleResultUrl; + return false; + } + } }); // attach config plugins and event middleware this.use(this.config); diff --git a/packages/snap-shared/src/MockData/autocomplete/8uyt2m/singleResult.json b/packages/snap-shared/src/MockData/autocomplete/8uyt2m/singleResult.json new file mode 100644 index 000000000..b67442fa9 --- /dev/null +++ b/packages/snap-shared/src/MockData/autocomplete/8uyt2m/singleResult.json @@ -0,0 +1,65 @@ +{ + "pagination": { + "totalResults": 1, + "page": 1, + "pageSize": 30, + "totalPages": 1 + }, + "results": [ + { + "id": "182146", + "mappings": { + "core": { + "uid": "182146", + "sku": "C-AD-W1-1869P", + "name": "Stripe Out White Off-The-Shoulder Dress", + "url": "/product/C-AD-W1-1869P", + "addToCartUrl": "/product/C-AD-W1-1869P", + "price": 48, + "msrp": 50, + "imageUrl": "https://searchspring-demo-content.s3.amazonaws.com/demo/fashion/product_images_large/4468_copyright_reddressboutique_2017__large.jpg", + "secureImageUrl": "https://searchspring-demo-content.s3.amazonaws.com/demo/fashion/product_images_large/4468_copyright_reddressboutique_2017__large.jpg", + "thumbnailImageUrl": "https://searchspring-demo-content.s3.amazonaws.com/demo/fashion/product_images_thumb_med/4468_copyright_reddressboutique_2017__thumb_med.jpg", + "rating": "5", + "ratingCount": "1111", + "description": "Are you Stripe Out of ideas for what to wear this weekend on that trip you've got coming up with your friends? Afraid you'll be the odd one out and everyone else will be all cute and trendy and there you'll be ... not trendy and wearing the same old things you've been wearing on this annual getaway for years? Lucky for you, here's the dress you've been searching for. Doesn't matter what else you pack (it does, you'll want to continue to shop with us, we were just being nice) this is the piece that will set you apart from everyone else (that is absolutely true, you will be a Goddess among women). Take that, bad fashion moments of the past! Striped dress features 3/4 sleeve bell sleeves with a partially elastic/open back. Model is wearing a small. • 97% Cotton 3% Spandex • Machine Wash Cold • Lined • Made in the USA", + "stockMessage": "In stock", + "brand": "Adrienne", + "popularity": "4461", + "caption": "Captions!" + } + }, + "attributes": { + "id": "b419ddfcad5f87ee49c786eb5f8621fd", + "intellisuggestData": "eJyyKK0sMcplYEgpSi0uZnDWdXTRDTfUNbQwswxgMGQwZDBgMDSyNGdIL8pMAQQAAP__9rsKZQ", + "intellisuggestSignature": "be0783431378a1700c8e0e498aa928696c08495713585d89fd975e3ba40c0c4d", + "product_type_unigram": "dress" + }, + "children": [] + } + ], + "filters": [], + "facets": [], + "sorting": [], + "merchandising": { + "campaigns": [], + "redirect": "", + "content": {} + }, + "search": { + "query": "dress" + }, + "autocomplete": { + "query": "dress", + "suggested": { + "text": "dress", + "type": "exact", + "source": "popular-query" + }, + "alternatives": [ + { + "text": "red dress" + } + ] + } +} \ No newline at end of file diff --git a/packages/snap-store-mobx/src/types.ts b/packages/snap-store-mobx/src/types.ts index c9c1430ab..f29896e9f 100644 --- a/packages/snap-store-mobx/src/types.ts +++ b/packages/snap-store-mobx/src/types.ts @@ -63,6 +63,30 @@ export type FinderFieldConfig = { levels?: string[]; }; +export type AutocompleteStoreConfigSettings = { + integratedSpellCorrection?: boolean; + initializeFromUrl?: boolean; + syncInputs?: boolean; + serializeForm?: boolean; + facets?: FacetStoreConfig & { + fields?: { + [field: string]: FacetStoreConfig; + }; + }; + trending?: { + limit: number; + showResults?: boolean; + }; + history?: { + limit: number; + showResults?: boolean; + }; + redirects?: { + merchandising?: boolean; + singleResult?: boolean; + }; +}; + // Autocomplete config export type AutocompleteStoreConfig = StoreConfig & { globals?: Partial; From 359e90115a4b6c40fdddf31909165be9a487a52f Mon Sep 17 00:00:00 2001 From: Dennis Konieczek Date: Tue, 3 Oct 2023 13:18:48 -0400 Subject: [PATCH 2/2] refactor: use AutocompleteStoreConfigSettings --- packages/snap-store-mobx/src/types.ts | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/snap-store-mobx/src/types.ts b/packages/snap-store-mobx/src/types.ts index f29896e9f..e3c4c74a1 100644 --- a/packages/snap-store-mobx/src/types.ts +++ b/packages/snap-store-mobx/src/types.ts @@ -92,28 +92,7 @@ export type AutocompleteStoreConfig = StoreConfig & { globals?: Partial; selector: string; action?: string; - settings?: { - integratedSpellCorrection?: boolean; - initializeFromUrl?: boolean; - syncInputs?: boolean; - serializeForm?: boolean; - facets?: FacetStoreConfig & { - fields?: { - [field: string]: FacetStoreConfig; - }; - }; - trending?: { - limit: number; - showResults?: boolean; - }; - history?: { - limit: number; - showResults?: boolean; - }; - redirects?: { - merchandising?: boolean; - }; - }; + settings?: AutocompleteStoreConfigSettings; }; // Recommendation config