No Preview

Sorry, but you either have no stories or none are selected somehow.

If the problem persists, check the browser console, or the terminal you've run Storybook from.

2.6.3 (24th October, 2024)

🪲 Fixes

  • useSearchResults was not updating the state when selected facets were provided on the initial state and no facets were in the response.

2.6.2 (15th October, 2024)

🪲 Fixes

  • keyword was not filled on Search Results events

2.6.1 (17th September, 2024)

🪲 Fixes

userId was not being passed on WidgetsProvider. This was not allowing to register anonymous users.

Example usage:

<WidgetsProvider userId={'custom'} env={'<environement>'} customerKey={'<customer key>'} apiKey={'<api key>'} > ... app contents </WidgetsProvider>

2.6.0 (11th September, 2024)

🚀 Features

Major templates re factor

All widget templates have been re factored in order to use the components provided by the @sitecore-search/cli package. widget templates are still created using the cli and when a new widget is created a folder labeled components is created in the same directory than the widget.

Before: 2 files were generated for a widget template (with the exception of tailwind, that only one file was generated).

  • One for the react component (index)
  • One for the styles (styles) On index, all widget template functionality was coupled on a single file. Now: Multiple files are generated for a widget template providing a better separation of concerns and reducing code duplication. For instance, on a Search Results widget template we have:
// index.tsx from Search Results widget ... import ArticleItemCard from '../components/ArticleHorizontalCard/index'; import Filters from '../components/Filter/index'; import QueryResultsSummary from '../components/QueryResultsSummary/index'; import ResultsPerPage from '../components/ResultsPerPage/index'; import SearchFacets from '../components/SearchFacets/index'; import SearchPagination from '../components/SearchPagination/index'; import SortOptions from '../components/SortOrder/index'; import Spinner from '../components/Spinner/index'; ...

and then on the file system:

image

🪲 Fixes

Geo location payload was not property built for complex filters such and and or.

Using single filter with geo location:

This was working ✅

const geoFilter = new FilterGeo('location', '30km', { lat: 10, lon: 10 }); query.getRequest().setSearchFilter(geoFilter);

Using a composable filter with Geo location filters within:

This was fixed 🛠️

const geoFilterA = new FilterGeo('location', '30km', { lat: 10, lon: 10 }); const geoFilterB = new FilterGeo('location', '30km', { lat: 11, lon: 20 }); const geoFilterC = new FilterGeo('location', '10km', { lat: 7, lon: 15 }); const filterOr = new FilterOr([geoFilterA, geoFilterB, geoFilterC]); query.getRequest().setSearchFilter(filterOr);

2.5.6 (2nd September, 2024)

🚀 Features

Added support for store in context.

Changing the store context will update the widgets in order to show items related to a specific store. Example:

// Set store context PageController.getContext().setStoreId('<id>'); PageController.getContext().setStoreGroupId('<group_id>');

Added store context values in event tracking functions

Store will also be sent together with the tracking information.

2.5.5 (5th August, 2024)

🪲 Fixes

Issues on preview search:

  • Widget was not closing modal after submitting (hitting enter key)
  • Widget was not closing modal after clicking on a suggestion.
  • Widget was not closing modal after clicking on an Item.

To make the item close work some changes are going to be needed:

For styled-components, on styled.ts (or js)

replace const PreviewSearchLink = styled.a by const PreviewSearchLink = styled(PreviewSearch.ItemLink)

For tailwind, css and cssModules:

Replace the anchor tag that wraps the Product/Article card by PreviewSearch.ItemLink. Properties should remain the same.

2.5.4 (23rd July, 2024)

🪲 Fixes

  • Fixed storybook typos
  • Added a bordered area for the components's demo.

2.5.3 (22nd July, 2024)

🚀 Features

New set of builtin components

Now SDK provides a set of components that were used on the Widget Templates in order to facilitate reusability. In the same way than templates, Widget Components can be created in different languages (Typescript and Javascript) and using different css implementations (tailwind, styled-components, css, css modules). The list of components can be found here

New cli command to create new builtin components

In addition to the reusable components, a new command in the cli has been added to facilitate the copy/creation of those. Having the @sitecore-search/cli package installed, the addition of a new component can be done by executing the following command:

npx sc-search new-component -w -s <styled|tailwind|css|cssModule> -l <typescript|javascript> -n <Component name> -o <destination/path>

or just

npx sc-search new-component

and cli wizard will guide during the creation.

The following is the complete list of parameters:

OptionAliasTypedescription
namenstringThe component name in our systems
languageljavascript or typescriptThe component language
stylingsstyled or css or cssModule or tailwind or plainThe component style
overwritewbooleanIf it is present, will delete existing content that matches and will create the component again
outputostringoutput directory for the component

2.5.2 (2nd July, 2024)

🔄 Changes

  • Moved order-cloud-sdk to peer dependencies

To reduce build size, now order-cloud-sdk is being loaded using dynamic imports. This gives the chance to the developer to avoid the inclusion of this libary in order to reduce bundle size. In order to accomplish this, here are some examples based on different setups:

Using Vite and Rollup:

// vite.config.ts import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig({ build: { rollupOptions: { external: ["ordercloud-javascript-sdk"], // exclude order-cloud from build }, }, plugins: [react()], });

Using next.js

// next.config.mjs /** @type {import('next').NextConfig} */ const nextConfig = { output: "export", webpack: function (config, options) { console.log(options.webpack.version); // Should be webpack v5 now config.externals.push("ordercloud-javascript-sdk"); return config; }, }; export default nextConfig;

Using webpack

Similar to next.js, ordercloud-javascript-sdk must be flagged as external.

🪲 Fixes

  • Widget view event was not sending entities We made a fix on our analytics. Widget view event was not reporting the entities shown.

2.5.1 (6th June, 2024)

🚀 Features

  • Add optional query initializer on ContentBlockWidget
{/* Render an HTML widget based on query parameters */} <HTMBlockWidget rfkId="home_hero" initializer={(query) => { const queryParams = new URLSearchParams(window.location.search); if (queryParams.has('q') && queryParams.get('q') !== null) { query.getRequest().setSearchQueryKeyphrase(queryParams.get('q')!); } }} />

🪲 Fixes

  • fixed properties using setWidget Examples:
import { Widget, setWidget } from '@sitecore-search/react'; import { Component } from 'path/to/component'; setWidget('myRfkId', 'entity', { component: Component, options: { props: { prop1, prop2 } } })
  • Removed internal environments Examples:
// Valid environment <WidgetProvider env="prod"> <WidgetProvider env="prodEu"> <WidgetProvider env="prodApse2">

2.5.0 (27th May, 2024)

🚀 Features

  • Added tailwind variation for templates.

2.4.2 (16th May, 2024)

🪲 Fixes

  • Events not passing context on server

2.4.1 (14th May, 2024)

🪲 Fixes

  • Removed mandatory validation of coordinate on geo filter
  • Issue using text behavior for facets. When the facet was not on the response the filter was not removed from the UI.

🔄 Changes

  • Point example projects to local ESM SDK builds

2.4.0 (29th April, 2024)

🚀 Features

  • Added support to get all the widgets that belong to a page. There are two ways to get the widgets per page: Using PageComponent container:
import { PageComponent, WidgetDataType, setWidget } from '@sitecore-search/react'; import MyComponent from './path/to/MyComponent'; const Search = () => { setWidget('rfkid_7', 'content', { component: MyComponent, type: WidgetDataType.SEARCH_RESULTS, }); return <PageComponent uri={'/search'} />; }; export default Search;

In this example we want to gather all the widgets added on /search. /search content should be configured in CEC. Using usePageWidgets hook

import { Widget, usePageWidgets, withPageWidgets } from '@sitecore-search/react'; const WidgetContainer = () => { const { isLoading, data: widgets = [] } = usePageWidgets('/home'); return ( <div> {isLoading && <div>Loading</div>} {!isLoading && widgets.map(({ rfkId, entity }: { rfkId: string; entity: string }) => { return <Widget rfkId={rfkId} key={`${rfkId}-${entity}`} entity={entity} />; })} </div> ); }; export default withPageWidgets(WidgetContainer);
  • Added geoWithin filter

A new type of filter related to Geo Location to search by a polygonal area. Examples:

// Using the standalone request import { WidgetRequest, FilterGeoWithin } from '@sitecore-search/data'; const geoWidget = new WidgetRequest('rfkid_7'); const coordinates = [ { lat: -28.281932, lon: -55.573635, }, { lat: -31.254713, lon: -50.928666, }, { lat: -16.181724, lon: -47.217881 }, ]; geoWidget.setSearchFilter(new FilterGeoWithin('location', coordinates));
// Using query property from a hook import { useSearchResults, FilterGeoWithin } from '@sitecore-search/react'; const { query } = useSearchResults(); const coordinates = [ { lat: -28.281932, lon: -55.573635, }, { lat: -31.254713, lon: -50.928666, }, { lat: -16.181724, lon: -47.217881 }, ]; query.getRequest().setSearchFilter(new FilterGeoWithin('location', coordinates));
// Setting initial values for a filter. import { useSearchResults, FilterGeoWithin } from '@sitecore-search/react'; const { query } = useSearchResults({ query: (query) => { const coordinates = [ { lat: -28.281932, lon: -55.573635, }, { lat: -31.254713, lon: -50.928666, }, { lat: -16.181724, lon: -47.217881 }, ]; query.getRequest().setSearchFilter(new FilterGeoWithin('location', coordinates)); }, });
  • Provide the ability to execute a function before triggering request to search api New parameter in WidgetProvider, requestMiddleware, to call a function before a request is performed. requestMiddleware: async() => void

Example:

<WidgetsProvider requestMiddleware={async () => { await myAsyncFunction();} }> ...app contents </WidgetsProvider>
  • Added new parameter on WidgetProvider to disable tracking

Added new parameter, trackConsent to enable/disable tracking consent.

const [tracking, setTracking] = useState(true); useEffect(() => { const response = prompt("Do you consent to data tracking?"); switch (response) { ... } }, []); <WidgetProvider trackConsent={tracking}> ...app contents </WidgetProvider>
  • Added campaign properties for context:

  • utm_source: string

  • utm_medium: string

  • utm_campaign: string

  • utm_term: string

  • utm_content: string

Example:

import { PageController } from '@sitecore-search/react'; PageController.getContext().setUtmSource('utm source'); PageController.getContext().setUtmMedium('utm medium'); PageController.getContext().setUtmCampaign('utm campaign'); PageController.getContext().setUtmTerm('utm term'); PageController.getContext().setUtmContent('utm content');
  • Add support for custom attributes on user context

Example:

PageController.getContext().setContextUserCustom({ customProp1: 'customProp1' });
  • Add support for keyphrase_fallback
const widget = new WidgetRequest('rfkid_6'); widget.setEntity('content'); widget.setSearchContent({}); widget.setSearchSuggestion([{ name: 'title_context_aware', keyphrase_fallback: true, max: 10 }]);
  • Add user agent to context when the SDK starts

🪲 Fixes

  • Added warnings if the user is trying to render a widget they have not defined beforehand.

🔄 Changes

  • Moved extractDomain function to data package.
  • Merged model package together with data.
  • Added examples app for data package.
  • Removed axios dependency to use native fetch.

2.2.3 (5th February, 2024)

🚀 Features

  • Added optional redirection handlers to PreviewSearch templates.

itemRedirectionHandler: An optional custom redirection handler that will be called when the user clicks on an item. itemRedirectionHandler?: (article: ArticleModel) => void; Examples

(item) => history.push(`/search?q=${item.id}`);
(item) => window.location.href = `/search?q=${item.id}`
(item: Article) => setRoute(`/custom/search/endpoint?q=${item.id}`);

submitRedirectionHandler: An optional custom submit handler that will be called when the user submits the form by pressing the enter key. submitRedirectionHandler?: (query: string) => void;

Examples

(query: string) => history.push(`/search?q=${query}`);
(query: string) => window.location.href = `/search?q=${query}`;
(query: string) => setRoute(`/custom/search/endpoint?q=${query}`);
  • Add store properties on Context: context.store.id: stringcontext.store.group_id: string

Example:

import { PageController } from '@sitecore-search/react'; ... PageController.getContext().setStoreId('my id'); PageController.getContext().setStoreGroupId('my group id');
  • Added groups to context user model

context.user.groups: Array<string>

Example:

import { PageController } from '@sitecore-search/react'; ... PageController.getContext().setUserGroups(['group1', 'group2']);

🪲 Fixes

  • Export useContentBlock
import { useContentBlock } from '@sitecore-search/react'; ... useContentBlock();

🔄 Changes

  • Added run method to DataProvider

Example: before

DataProvider.get(request.toJson());

now

DataProvider.run(request);

2.2.2 (9th January, 2024)

🚀 Features

  • Added a new hook, useSearchResultsIncrementalResults to get more results without loosing the current ones (this is useful for infinite scroll or load more experiences).

Usage example:

import { useSearchResultsIncrementalResults } from '@sitecore-search/react'; ... const articles = useSearchResultsIncrementalResults();
  • Added a new primitive, SearchResultsLoadMore. This is a Load More button to be used together with useSearchResultsIncrementalResults (by clicking it, will get the following page of results). Check this on the Primitives section.

  • Added a new SearchResults template using useSearchResultsIncrementalResults and SearchResultsLoadMore

🔄 Changes

  • Fixed an issue that was not pining SDK internal dependencies.

2.2.1 (2nd January, 2024)

🚀 Features

  • Adds abort signal for debounced requests
  • Improves experimental release PR changelog

🔄 Changes

  • Pin SDK package dependencies
  • Bump lerna and vite dev dependencies based on security audits (Snyk)

2.1.1 (10th November, 2023)

🪲 Fixes

  • Fixed filter not.
  • New Preview Search was not firing appear events.

2.1.0 (8th November, 2023)

🚀 Features

  • Range filters UI primitives, checkout docs.

  • Widget Config Parameter for query hooks

Example:

useSearchResults<ArticleModel, InitialState>({ config: { facets: { price: { type: 'range' }, // This tells the sdk to handle price as a range brand: { type: 'text' }, // This tells the sdk to allow match by text color: { type: 'valueId' } // This tells the sdk to match by facet id } } });

Reference

  • Initial values for Facets

Now facets can be initialized by sending initial values as parameters. Facets can be set as ranges, text or value id, depending on the facet configuration.

Example:

useSearchResults<ArticleModel, InitialState>({ state: { selectedFacets: [{ // Facet needs to be configured as range facetId: 'price', min: 0, max: 10, }, { // Facet needs to be configured as text facetId: 'brand', facetValueText: 'Brand 1', }, { // Facet needs to be configured as valueId or can be ommited. facetId: 'color', facetValueId: 'facetIdForColor' }] }, });
  • PreviewSearch primitive Brand new implementation for PreviewSearch primitives with better accessibility support and simpler structure.

Reference

🪲 Fixes

  • Fixed support for subdomain configurations.

🔄 Changes

  • Adds integration tests for create-react-app, next and vite (javasript & typescript)
  • Experimental releases support
  • Added date to release notes
  • Updated SearchResults templates
  • Added removeFilter action (onFilterClick will be deprecated).

2.0.0

🚀 Features

  • [BREAKING CHANGE] Improves Widget management

  • [BREAKING CHANGE] Moves to ESM

We're excited to announce a significant improvement in the library's architecture. Starting from this version, we have moved the entire library to ECMAScript Modules (ESM). This move brings several benefits, including better performance, improved tree-shaking capabilities, and enhanced compatibility with modern JavaScript tools and bundlers.

With the adoption of ESM, you'll be able to take advantage of a more streamlined and standardized module system, making it easier to integrate the library into various project setups. It also opens up opportunities for better code optimization and module isolation.

However, this change does come with a breaking impact on older environments and bundlers that do not fully support ESM. As a result, it's crucial to ensure that your project's build tools and runtime environment are compatible with ESM. Most modern bundlers like Webpack and Rollup have native support for ESM, so updating should be relatively straightforward.

We understand that upgrading to ESM may require some adjustments to your build configuration or dependencies. To help you through this process, we have provided detailed migration instructions in the updated documentation. Additionally, our support team is always available to assist you with any questions or issues you may encounter during the transition.

We believe that this move to ESM will bring long-term benefits to both the library's maintainability and your development experience. As always, we greatly appreciate your support and feedback as we continue to improve and evolve the library. Happy coding with the new ESM-powered version! 🚀

  • New Widgets Added: HTMLBlock, SEO, and Banner

We're excited to introduce three new widgets: HTMLBlock, SEO, and Banner. Although pages support is still in development, you can already make use of these powerful new widgets in your app. Here's an example of how to use them:

Example

function App() { return ( <WidgetsProvider {...DISCOVER_CONFIG}> <HTMBlockWidget rfkId="rfkid_15" /> <SEOWidget rfkId="rfkid_9" /> <Banner rfkId="rfkid_10" /> </WidgetsProvider> ); }
  • Renamed context to state in all widget hooks for better clarity and consistency

Before

const { ... context: { page }, ... } = useSearchResults();

After:

const { ... state: { page }, ... } = useSearchResults(); ``` - Simplified widget hook initialization for all widgets ### Before: ```typescript const { ... } = useSearchResults(() => { query.getRequest().setSearchGroupBy('my_group'); // optionally return { page: 10, }; });

After:

const { ... } = useSearchResults({ query: (query) => query.getRequest().setSearchGroupBy('my_group'), // optionally state: { page: 10, }, });
  • Enhanced Widget hook typing:

To provide even stronger typings when defining the widget, we have improved the type interface for all widget hooks. This allows you to type the initialized state (if any) and ensures proper type checking for the current state. We have also exposed some helpful type helpers to assist with the typing process. Here's an example of how you can use it:

Example:

import type { SearchResultsInitialState, SearchResultsStoreState } from '@sitecore-search/react'; type ArticleModel = { id: string; type: string; title: string; subtitle: string; url: string; content_text: string; image_url: string; }; type ArticleSearchResultsProps = { defaultSortType?: SearchResultsStoreState['sortType']; defaultPage?: SearchResultsStoreState['page']; defaultItemsPerPage?: SearchResultsStoreState['itemsPerPage']; }; type InitialState = SearchResultsInitialState<'itemsPerPage' | 'page' | 'sortType'>; const MyWidget = ({ defaultSortType = 'featured_desc', defaultPage = 1, defaultKeyphrase = '', defaultItemsPerPage = 24, }: ArticleSearchResultsProps) => { const { state: { itemsPerPage, page, sortType, keyphrase }, } = useSearchResults<ArticleModel, InitialState>({ state: { sortType: defaultSortType, page: defaultPage, itemsPerPage: defaultItemsPerPage, }, }); ...

With this enhancement, you can see that the state properties and un-initialized properties like keyphrase are correctly typed

2023-07-28 at 12 34

  • Widget appear event tracking improvement:

Previously, the SDK was tracking when the widget appears in the viewport transparently using an old React/React-DOM feature (findDOMNode). However, this feature is being deprecated in React and will eventually be removed. To adapt to this change, starting from this version, developers will need to manually reference the node that needs to be tracked. Don't worry, though, the SDK will still automatically trigger the event once the node is referenced. Here's how you can use the new widgetRef prop to track the widget:

const { widgetRef, ... } = usePreviewSearch(); return ( <div ref={widgetRef}> ... </div> );
  • Changes authorization process

Starting from this version, we have deprecated the useToken configuration property, and we encourage using it only when absolutely required. With this change, there's no need to include useToken in your configuration if API access doesn't specifically demand an access token.

Previously, useToken was intended for cases where API access required an access token. However, in most browser-based scenarios, this extra round-trip to the API was unnecessary and added overhead, resulting in a slightly slower experience.

Instead, we have introduced a more targeted approach with the useAccessToken property. This allows you to specify and handle access token usage more selectively and efficiently. Use useAccessToken only if your API access indeed necessitates an access token.

As always, our documentation has been updated to reflect these changes, and our support team is available to assist you should you have any questions or concerns. We hope that this enhancement empowers you to make informed decisions regarding authorization in your application.

  • Enables noImplicitAny

We've enabled the noImplicitAny option by default in this version. This TypeScript compiler option enforces stricter type checking by requiring explicit type annotations for variables and function return types where TypeScript cannot infer the type.

Enabling noImplicitAny promotes safer code practices and helps catch potential type errors during compilation. While adopting this option may require adding more type annotations, it ultimately leads to a more robust and maintainable codebase.

Embrace the power of TypeScript's static type checking with noImplicitAny, and enjoy a more streamlined development process. If you need any assistance with this feature, refer to our documentation or reach out to our support resources.

🚀 Features

  • Add CSS & CSS Modules templates styling variants in cli
  • Added HighlightComponent
  • Added Preview Seearch suggestion's click action
  • Removed filtering options & default behavior to or
  • New action_sub_type funnel events field
  • Updates suggestions click event schema
  • Added apse2 to allowed environments
  • Enables strictFunctionTypes

🪲 Fixes

  • Fix for geo filters
  • Handles possible undefined search limit
  • Updates search result context hooks
  • Fixes Logger instance creation
  • Fixes response sanitization
  • Fixes query client prop in WidgetsProvider
  • Reset offset and page in search result widget
  • Fixes un-listened query context
  • Reset filter when keyphrase change** in Preview Search widget
  • Several template enhancements
  • Include onKeyphraseChange action on previewSearch action definition
  • Rename numProducts property in ResultsPerPageChangedPayload to numItems
  • Improves filter change event value
  • Fix type properties in ui components
  • Fixes query highlight model

-- These changes and additions to the library will greatly enhance your development experience and empower you to build even more dynamic and feature-rich applications. If you have any questions or feedback, please don't hesitate to reach out to us. Happy coding! 🎉