- 404 - This link is broken... sorry.
+ This link may no longer exist. Please find the content you are looking for in the side navigation or attempt to search for it.
\ No newline at end of file
diff --git a/docs/ABOUT.md b/docs/ABOUT.md
index 926994f77b..00dd1118be 100644
--- a/docs/ABOUT.md
+++ b/docs/ABOUT.md
@@ -1,15 +1,39 @@
-## What is Snap?
+# Snap Documentation
-Snap is not an acronym! Snap is Searchspring's open source SDK for integrating into front end web apps. It replaces the previous version - v3.
+Snap is not an acronym! Snap is an open source SDK for building e-commerce experiences powered by Searchspring.
-Snap supports Searchspring's latest features and API release for lightning fast performance.
+The SDK includes multiple core packages published to npm that in combination with each other, provide the complete front-end tooling for building e-commerce experiences with Searchspring. However to simplify usage, the `@searchspring/snap-preact` package is an abstraction that combines all core packages into a single dependency in combination with Preact to render the UI. This documentation is primarily focused on the usage of this package.
-Snap also supports integration with modern front end frameworks such as React and Vue. In addition, we've also released a full [React component library](https://searchspring.github.io/snap/#/components-preact) to get you up and running quickly.
+## Getting Started
+This documentation is organized into two sections: Snap Integration and API Integration. Depending on how you're going to integrate Snap, you'll want to reference the correct section to get started.
-## Getting Started
+
+
+Additionally, the Reference section contains the most comprehensive and technical material that is common between all integration types and is linked to from other sections.
+
+### Snap Integration
+
+A "Snap Integration" is a project that uses the `Snap` export from the `@searchspring/snap-preact` package to build a storefront integration. It provides the ability to create multiple controllers, custom plugins, and full custom markup to match the storefront markup and inherit styles. It is the most flexible and powerful way to integrate Searchspring into your storefront.
+
+An example Snap Integration project can be found [here](https://github.com/searchspring-implementations/demo.shopify).
+
+Continue by referencing the [Snap Setup](https://searchspring.github.io/snap/snap-setup) section.
+
+### Snap Templates Integration
+
+A "Snap Templates Integration" is a project that uses the `SnapTemplates` export from the `@searchspring/snap-preact` package to build a storefront integration. It is an abstraction of the `Snap` integration that limits the available configuration and does not provide access to the entire project markup.
+
+Instead, it is based on choosing an optimimzed and prebuilt template and theme while only customizing slight layout changes, theme variables, result card markup, and general style declarations. This integration type allows for a rapid integration of Searchspring to your storefront.
+
+Continue by referencing the [Snap Templates Integration](https://searchspring.github.io/snap/templates-about) documentation.
+
+### API Integration
+
+An "API Integration" is a project that utilizes the Searchspring APIs directly to integrate into your custom storefront project. Although not required, we recommend using just the `@searchspring/snap-client` package to fetch data from Searchspring APIs.
+
+Continue by referencing the [API Integration](https://searchspring.github.io/snap/snap-client) section.
-Check out the [Getting Started](https://searchspring.github.io/snap/#/start-setup) guide to jump right in!
## Contributing
@@ -18,8 +42,3 @@ Snap is open source! The repository can be found on [Github](https://github.com/
We invite your participation through Github issues, discussions and pull requests!
Please reference Searchspring's [Community Contribution Guidelines](https://github.com/searchspring/community/blob/main/CONTRIBUTING.md) before contributing.
-
-
-## Documentation
-
-For all Snap documentation, see the [Snap Docs](https://searchspring.github.io/snap/).
diff --git a/docs/ADVANCED.md b/docs/ADVANCED.md
deleted file mode 100644
index aaae46d582..0000000000
--- a/docs/ADVANCED.md
+++ /dev/null
@@ -1,8 +0,0 @@
-## Advanced Snap Usage
-
-If a controller or service is needed with configuration outside of what is permited by the Snap abstraction layer, one can be created by following the docs in this section.
-
-It is recommended to use the Snap abstraction layer packages whenever possible - these packages quickly create Snap controllers using a config based interface. Underneath the hood the Snap abstraction packages utilize all of the core Snap packages that will be outlined here.
-
-### Snap abstraction Packages
-[@searchspring/snap-preact](https://github.com/searchspring/snap/tree/main/packages/snap-preact)
\ No newline at end of file
diff --git a/docs/ADVANCED_AUTOCOMPLETE.md b/docs/ADVANCED_AUTOCOMPLETE.md
deleted file mode 100644
index 9eee744951..0000000000
--- a/docs/ADVANCED_AUTOCOMPLETE.md
+++ /dev/null
@@ -1,102 +0,0 @@
-## Autocomplete
-To set up Autocomplete using Snap, we'll need to create a `AutocompleteController` instance, which requires `AutocompleteControllerConfig` and `ControllerServices` objects to instantiate. For more details see the [`AutocompleteController docs`](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Autocomplete).
-
-### Config (AutocompleteControllerConfig)
-Let's define an `AutocompleteControllerConfig` object:
-
-```typescript
-const autocompleteConfig = {
- id: 'autocomplete',
- selector: '#search_query',
- globals: {
- suggestions: {
- count: 4,
- },
- pagination: {
- pageSize: 6,
- },
- },
- settings: {
- initializeFromUrl: true,
- syncInputs: false,
- facets: {
- trim: true
- },
- },
-}
-```
-
-### Autocomplete Controller Services
-The `ControllerServices` object contains all of the controller's dependencies. Note the difference between SearchController's ControllerServices is the different store. Here we are using `AutocompleteStore`
-
-Note that the `UrlManager` is utilizing the `UrlTranslator` which will use `'q'` as the URL query parameter. This can be overwritten to use `'search_query'` by providing a by providing a `parameters.core.query` [config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) such as in this example:
-
-```typescript
-const autocompleteUrlManager = new UrlManager(new UrlTranslator({ parameters: core: { query: { name: 'search_query' } } }), reactLinker).detach();
-const autocompleteControllerServices = {
- client,
- store: new AutocompleteStore(autocompleteConfig, { urlManager: autocompleteUrlManager }),
- urlManager: new UrlManager(new UrlTranslator({ queryParameter: 'search_query' }), reactLinker),
- eventManager: new EventManager(),
- profiler: new Profiler(),
- logger: new Logger(),
- tracker
-}
-```
-
-The translator type should be the same between Autocomplete and Search Controllers in order for compatible URLs to be generated.
-
-Note: `client` and `tracker` are shared services and are defined in previous steps.
-
-### Instantiation
-With the `AutocompleteControllerConfig` and `ControllerServices` defined, we can now create an instance of a `AutocompleteController`.
-
-```typescript
-const autocompleteController = new AutocompleteController(autocompleteConfig, autocompleteControllerServices);
-```
-
-### Middleware
-Autocomplete supports middleware to hook into various events using `plugin` and `on` methods. See [Search Middlewear](https://github.com/searchspring/snap/blob/main/docs/SEARCH.md) for usage
-
-
-### DomTargeter
-Similar to Search DomTargeter, the following example shows how to use the DomTargeter with an `AutocompleteController` and passing that controller as a prop to a `Autocomplete` component (not shown). After the target has been found it injects a new element ('.ss-ac-target') and then uses the Preact render function to render the component into the newly created element.
-
-For further usage and documentation, see [DomTargeter](https://github.com/searchspring/snap/tree/main/packages/snap-toolbox/src/DomTargeter).
-
-```typescript
-new DomTargeter(
- [
- {
- selector: autocompleteController.config.selector, // input element that we are binding to
- inject: {
- action: 'after', // injecting an element after the input
- element: (target, origElement) => {
- const acContainer = document.createElement('div');
- acContainer.id = 'ss-ac-target';
- acContainer.addEventListener('click', (e) => {
- e.stopPropagation();
- });
- return acContainer;
- },
- },
- },
- ],
- (target, injectedElem, inputElem) => {
- // bind to config selector
- autocompleteController.bind();
- render(, injectedElem);
- }
-);
-```
-
-### Initialize
-Optionally initialize the controller by invoking its `init` method. This will subscribe to any `UrlManager` state changes and fire the `init` event and any attached middleware. This will happen automatically when invoking the `search` method for the first time.
-
-```typescript
-autocompleteController.init();
-```
-
-### Bind inputs
-
-Invoking the AutocompleteController `bind` method will bind the controller instance to all selector elements found. In this example, this occurs in the DomTargeter callback method (`onTarget`) when a selector is found.
diff --git a/docs/ADVANCED_CLIENT.md b/docs/ADVANCED_CLIENT.md
deleted file mode 100644
index 553b40ae92..0000000000
--- a/docs/ADVANCED_CLIENT.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Snap Client
-Next, let's define Snap Client as it is required for all Snap controller instances. Typically the Client is shared across all Snap controllers.
-
-```typescript
-const client = new Client(globals);
-```
-
-The Snap Client requires `ClientGlobals` for instantiation.
-
-### Global Config
-The `ClientGlobals` object can be used to set client search parameters that will apply to all requests made with the client (and subsequently any controllers using the client as a service). Typically only the `siteId` will be set here, but could be used for setting globally utilized background filters and/or sorts as well.
-
-You can find your Searchspring `siteId` in the [Searchspring Management Console](https://manage.searchspring.net) and define it directly:
-
-```typescript
-const globals = {
- siteId: 'a1b2c3',
-};
-```
diff --git a/docs/ADVANCED_FINDER.md b/docs/ADVANCED_FINDER.md
deleted file mode 100644
index 84d3eb6cf2..0000000000
--- a/docs/ADVANCED_FINDER.md
+++ /dev/null
@@ -1,123 +0,0 @@
-## Finder
-To set up a product Finder using Snap, we'll need to create a `FinderController` instance, which requires `FinderControllerConfig` and `ControllerServices` objects to instantiate. For more details see the [`FinderController docs`](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Finder).
-
-### Config (FinderControllerConfig)
-There are two types of Finder configurations, a Hierarchy and Non-Hierarchy. The difference is the type of field being used and how it is configured in the Searchspring Management Console.
-
-#### Hierarchy Config
-To use a Hierarchy configuration, ensure that the config's `fields` array contain a single entry, and that the field is of type `hierarchy` in the Searchspring Management Console. Here is an example of a Hierarchy `FinderControllerConfig` object:
-
-```typescript
-const finderConfig = {
- id: 'finder',
- url: '/search'
- fields: [{
- field: 'ss_tire',
- label: 'Wheel Finder',
- levels: ['Year', 'Make', 'Model', 'Wheel Size']
- }]
-}
-```
-
-#### Non-Hierarchy Config
-To use a Non-Hierarchy configuration, multiple `fields` are specified. All fields must have a `type` or `value` and NOT `hierarchy`. Facet types can be configured in the Searchspring Management Console. Here is an example of a Non-Hierarchy `FinderControllerConfig` object:
-
-```typescript
-const finderConfig = {
- id: 'finder',
- url: '/search',
- fields: [
- {
- field: 'custom_wheel_size',
- label: 'Size'
- },
- {
- field: 'custom_wheel_width',
- label: 'Width'
- },
- {
- field: 'custom_wheel_bolt_pattern',
- label: 'Bolt Pattern'
- },
- {
- field: 'custom_color',
- label: 'Color'
- }
- ]
-};
-```
-
-Note: When using fields that are not of hierarchy type, `levels` are not required.
-
-### ControllerServices
-The `ControllerServices` object contains all of the controller's dependencies.
-
-Note that the `UrlManager` is created separately because it is a shared dependency; it is also a service needed for the `FinderStore`. The `UrlManager` is utilizing the `UrlTranslator` which will use `'q'` as the default URL query parameter. This can be overwritten to use `'search_query'` by providing a `parameters.core.query` [config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) such as in this example:
-
-```typescript
-const finderUrlManager = new UrlManager(new UrlTranslator({ parameters: core: { query: { name: 'search_query' } } }), reactLinker).detach(0);
-const finderControllerServices = {
- client,
- store: new FinderStore(finderConfig, { urlManager: finderUrlManager }),
- urlManager: finderUrlManager,
- eventManager: new EventManager(),
- profiler: new Profiler(),
- logger: new Logger(),
- tracker
-}
-```
-
-Note: `client` and `tracker` are shared services and are defined in previous steps.
-
-### Instantiation
-With the `FinderControllerConfig` and `ControllerServices` defined, we can now create an instance of a `FinderController`.
-
-```typescript
-const finderController = new FinderController(finderConfig, finderControllerServices);
-```
-
-### Middleware
-Finders support middleware to hook into various events using `plugin` and `on` methods. See [Search Middlewear](https://github.com/searchspring/snap/blob/main/docs/SEARCH.md) for usage.
-
-
-### DomTargeter
-Similar to Search DomTargeter, the following example shows how to use the DomTargeter with a `FinderController` and passing that controller as a prop to a `Finder` UI component (not shown). It uses the Preact render function to render the component after the target has been found.
-
-For further usage and documentation, see [DomTargeter](https://github.com/searchspring/snap/tree/main/packages/snap-toolbox/src/DomTargeter).
-
-```typescript
-const finderTarget = new DomTargeter(
- [
- {
- selector: '#finder-target-selector', // CSS selector for element to render component into
- },
- ],
- async (target, elem) => {
- // await search after target element is found
- await finderController.search();
-
- render(, elem);
- }
-);
-```
-
-### Initialize
-Optionally initialize the controller by invoking its `init` method. This will subscribe to any `UrlManager` state changes and fire the `init` event and any attached middleware. This will happen automatically when invoking the `search` method for the first time.
-
-```typescript
-finderController.init();
-```
-
-### Search
-After the controller has been initialized, the search method must be invoked for the finder to fetch its initial data. In the example above, this is being invoked in the DomTargeter.
-
-```typescript
-finderController.search();
-```
-
-### Find
-After selection(s) have been made, the user will click on a 'Find' button. This click event should invoke the `find` method of the Finder controller which will redirect to the specified `url` in the config with it's selection parameters attached.
-
-```typescript
-finderController.find();
-```
diff --git a/docs/ADVANCED_INSTALLATION.md b/docs/ADVANCED_INSTALLATION.md
deleted file mode 100644
index 28df5332e0..0000000000
--- a/docs/ADVANCED_INSTALLATION.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## Installation
-
-```shell
-npm install --save @searchspring/snap-client @searchspring/snap-url-manager @searchspring/snap-event-manager @searchspring/snap-profiler @searchspring/snap-logger @searchspring/snap-tracker @searchspring/snap-toolbox @searchspring/snap-controller @searchspring/snap-store-mobx
-```
-
-```typescript
-import { Client } from '@searchspring/snap-client';
-import { UrlManager, UrlTranslator, reactLinker } from '@searchspring/snap-url-manager';
-import { EventManager } from '@searchspring/snap-event-manager';
-import { Profiler } from '@searchspring/snap-profiler';
-import { Logger } from '@searchspring/snap-logger';
-import { Tracker } from '@searchspring/snap-tracker';
-import { DomTargeter } from '@searchspring/snap-toolbox';
-import { SearchController, AutocompleteController, FinderController } from '@searchspring/snap-controller';
-import { SearchStore, AutocompleteStore, FinderStore } from '@searchspring/snap-store-mobx';
-```
diff --git a/docs/ADVANCED_SEARCH.md b/docs/ADVANCED_SEARCH.md
deleted file mode 100644
index 6477a8eda6..0000000000
--- a/docs/ADVANCED_SEARCH.md
+++ /dev/null
@@ -1,150 +0,0 @@
-
Search
-
-To set up Search using Snap, we'll need to create a `SearchController` instance, which requires `SearchControllerConfig` and `ControllerServices` objects to instantiate. For more details see the [`SearchController docs`](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Search).
-
-### Config (SearchControllerConfig)
-Let's define a `SearchControllerConfig` object:
-```typescript
-const searchConfig = {
- id: 'search',
- globals: {
- filters: [],
- },
- settings: {
- redirects: {
- merchandising: true,
- singleResult: true,
- },
- facets: {
- trim: true,
- }
- },
-};
-```
-
-
-### Category Pages / Background Filters
-Optionally, apply filters from the page's content to the SearchControllerConfig `globals.filters` property. The controller globals are similar to the client globals in that all search requests will include the parameters specified. This can be used to configure category/brand pages, or other special filtering to apply to the current page's search requests.
-
-For example, if a global variable `snapConfig` exists on the page (must be defined prior to our Snap script):
-
-```html
-
-```
-
-```typescript
-if (snapConfig?.category) {
- searchConfig.globals.filters.push({
- type: 'value',
- background: true,
- field: 'categories_hierarchy',
- value: snapConfig.category.value,
- });
-}
-```
-
-
-### ControllerServices
-The `ControllerServices` object contains all of the controller's dependencies.
-
-Note that the `UrlManager` is created separately because it is a shared dependency; it is also a service needed for the `SearchStore`. The `UrlManager` is utilizing the `UrlTranslator` which will use `'q'` as the default URL query parameter. This can be overwritten to use `'search_query'` by providing a `parameters.core.query` [config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) such as in this example:
-
-```typescript
-const searchUrlManager = new UrlManager(new UrlTranslator({ parameters: core: { query: { name: 'search_query' } } }), reactLinker);
-const searchControllerServices = {
- client,
- store: new SearchStore(searchConfig, { urlManager, searchUrlManager }),
- urlManager: searchUrlManager,
- eventManager: new EventManager(),
- profiler: new Profiler(),
- logger: new Logger(),
- tracker
-}
-```
-
-Note: `client` and `tracker` are shared services and are defined in previous steps.
-
-### Instantiation
-With the `SearchControllerConfig` and `ControllerServices` defined, we can now create an instance of a `SearchController`.
-
-```typescript
-const searchController = new SearchController(searchConfig, searchControllerServices);
-```
-
-
Middleware
-
-Now that our `SearchController` is instantiated (using `searchController` variable), we can optionally attach middleware to hook into various events. There are two ways of doing this, using the Controller's `on` or `plugin` methods.
-
-#### via `on` method:
-
-```typescript
-searchController.on('afterStore', async ({ controller }, next) => {
- controller.log.debug('store', controller.store.toJSON());
- await next();
-})
-```
-
-#### via `plugin` method (to attach groups of middleware):
-
-```typescript
-const middleware = (controller) => {
- controller.on('init', async({ controller }, next) => {
- controller.log.imageText({
- url: 'https://searchspring.com/wp-content/themes/SearchSpring-Theme/dist/images/favicons/favicon.svg',
- text: 'snap integration initialized',
- style: `color: ${controller.log.colors.indigo}; font-weight: bold;`,
- });
-
- await next();
- });
- // log the store
- controller.on('afterStore', async({ controller }, next) => {
- controller.log.debug('store', controller.store.toJSON());
- await next();
- });
-};
-
-searchController.plugin(middleware);
-```
-
-
DomTargeter
-
-`DomTargeter` is a utility for rending components in specified DOM targets. The following example shows how to use the DomTargeter with a `SearchController` and passing that controller as a prop to a `Content` component (not shown). It uses the Preact render function to render the component after the target has been found.
-
-For further usage and documentation, see [DomTargeter](https://github.com/searchspring/snap/tree/main/packages/snap-toolbox/src/DomTargeter).
-
-```typescript
-const contentTarget = new DomTargeter(
- [
- {
- selector: '#searchspring-content', // CSS selector for element to render component into
- },
- ],
- (target, elem) => {
- // run search after finding target
- controller.search();
- render(, elem);
- }
-);
-```
-
-### Initialize
-Optionally initialize the controller by invoking its `init` method. This will subscribe to any `UrlManager` state changes and fire the `init` event and any attached middleware. This will happen automatically when invoking the `search` method for the first time.
-
-```typescript
-searchController.init();
-```
-
-### Perform Search
-
-Invoking the SearchController `search` method will perform a search. In this example, this is being done after the Domtargeter finds the element selector `#searchspring-content`. Simultaneously the `Content` component will be rendered into the element selector.
diff --git a/docs/ADVANCED_TRACKER.md b/docs/ADVANCED_TRACKER.md
deleted file mode 100644
index b20116e7a1..0000000000
--- a/docs/ADVANCED_TRACKER.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Snap Tracker
-The Snap Tracker, like the Client, is required for all Snap controller instances and is shared across all Snap controllers.
-
-```typescript
-const tracker = new Tracker(globals);
-```
-
-The Snap Tracker requires `TrackerGlobals` for instantiation.
-
-### Global Config
-The `TrackerGlobals` object requires only a `siteId`.
-
-You can find your Searchspring `siteId` in the [Searchspring Management Console](https://manage.searchspring.net) and define it directly:
-
-```typescript
-const globals = {
- siteId: 'a1b2c3',
-};
-```
\ No newline at end of file
diff --git a/docs/BUILD_DEPLOY.md b/docs/BUILD_DEPLOY.md
new file mode 100644
index 0000000000..a7a7f6b884
--- /dev/null
+++ b/docs/BUILD_DEPLOY.md
@@ -0,0 +1,189 @@
+# Building
+
+To build the project, run the following command:
+
+```sh
+npm run build
+```
+
+The build output files are placed in the `dist` directory. Each project is configured to use Webpack to build the project files and will build two sets of files: A modern build and a universal build.
+
+## Deploy
+
+If you are managing the project and repository (also referred to as "Self-Snap"), you will need handle the deployment of the build files to your CDN or hosting provider. (ie. Shopify, BigCommerce, Magento, AWS S3, etc..)
+
+If the URL above is used it will result in a 403 Error.
+
+To host your own build files follow the below steps in your project.
+
+1. Ensure you have changed directories so that you are in the root of the project directory
+2. In your terminal run the command `npm run build`, will output build files to `./dist`
+3. Navigate to `./dist` and copy the generated build files
+4. Go to the codebase of your E-commerce platform (Shopify, BigCommerce, Magento, etc.) and copy/paste the generated build files in a directory (most platforms have an ***assets*** directory)
+5. On the frontend of the site, add a script block as outlined in the [integration](https://searchspring.github.io/snap/build-deploy-integration) section - be sure to change the `src` attribute to point to the `bundle.js` file and align the URL with your self-hosted build files (eg: /assets/bundle.js)
+
+
+
+
+## Deploy to Searchspring CDN
+
+**Deploying to Searchspring CDN is only possible if the repository is managed by the Searchspring [Github organization](https://github.com/searchspring-implementations)**. Repositories in this organization are typically managed by the Searchspring professional services team and deployed via a CI/CD pipeline using the [snap-action](https://github.com/searchspring/snap-action) Github Action. An invitation can be requested for collaboration.
+
+Github action runs triggered on the default branch `production` will build and deploy bundle files to this URL:
+
+`https://snapui.searchspring.io/[your_site_id]/bundle.js`
+
+Builds on different branch names will be deployed to:
+
+`https://snapui.searchspring.io/[your_site_id]/[branch]/bundle.js`
+
+### Github Repository Requirements
+
+- Repository must be managed by the Searchspring [Github organization](https://github.com/searchspring-implementations)
+- Repository must have a default branch named `production`
+- Repository must have repository secrets for each siteId in the repository. Found at `https://github.com/[owner]/[repository]/settings/secrets/actions`
+ - Secret Key Name: `WEBSITE_SECRET_KEY_[SITEID]` where `[SITEID]` should be replaced with the 6 character alphanumeric siteId found in the [Searchspring Management Console](https://manage.searchspring.net). For example: `WEBSITE_SECRET_KEY_ABC123`
+ - Value: `secretKey` located adjacent to the siteId in the [Searchspring Management Console](https://manage.searchspring.net)
+- Repository must have a `snap-action` workflow file in the `.github/workflows` directory. See section below.
+- Repository must have a `package.json` file that contains all siteIds associated with this project. See section below.
+
+#### Github Action
+
+The [snap-action](https://github.com/searchspring/snap-action/) can be used by creating a new github workflow file (ie. `.github/workflows/deploy.yml`)
+
+The Snap Action requires additional parameters not shown in the example below. See [snap-action](https://github.com/searchspring/snap-action/) for additional documentation.
+
+```yml
+on: [push, pull_request]
+
+jobs:
+ Publish:
+ runs-on: ubuntu-latest
+ name: Snap Action
+ steps:
+ - name: Checkout action
+ uses: actions/checkout@v4
+ with:
+ repository: searchspring/snap-action
+ - name: Run @searchspring/snap-action
+ uses: ./
+```
+
+#### Project package.json configuration
+
+The package.json file must contain all siteIds associated with this project. This data is also used by the Snapfu CLI for various features.
+
+
+
+Single siteId example:
+
+```json
+{
+ ...
+ "searchspring": {
+ "siteId": {
+ "abc123": {
+ "name": "site1.com"
+ }
+ },
+ }
+}
+```
+
+Multi siteId example:
+
+```json
+{
+ ...
+ "searchspring": {
+ "siteId": {
+ "abc123": {
+ "name": "site1.com"
+ },
+ "def456": {
+ "name": "site1.com.au"
+ }
+ },
+ }
+}
+```
+
+### Branch Overrides
+
+This functionality is only currently possible with Searchspring managed Snap repositories (https://github.com/searchspring-implementations).
+
+While browsing a page that contains a Snap integration, appending the `?searchspring-preview=[branchname]` query parameter to the URL will stop the execution of the existing script, and load the build from the `[branchname]` branch `https://snapui.searchspring.io/[siteid]/[branchname]/bundle.js`
+
+You will see an interface overlay on the bottom right of the viewport indicating if successful and details of the build.
+
+
+
+This will also be persisted across page navigation. To stop previewing a branch build, you must click the `Stop Preview` button in the interface or clear the `ssBranch` cookie. The interface can also be minimized.
+
+## Modern vs Universal Builds
+
+If your project is hosted in Searchspring's Github organization, we'll automatically handle serving the appropriate version of the build files for you. However if you are hosting your own build files, you can either:
+
+- only serve the modern build to all users, however this will break functionallity for IE11.
+- change the build targets, although this may impact core web vitals negatively.
+- serve the appropriate bundle via a check of the userAgent. More information below.
+
+Always serve the build to modern browsers and the universal build only to legacy browsers. Serving universal builds to modern browsers negatively impacts Core Web Vitals:
+
+```html
+
+
+
+
+
+```
+
+The modern build is used for projects targeting the latest browsers supporting the latest JavaScript features (ES6 and above). Example modern build files: `bundle.js` & `bundle.chunk.[hash].js`
+
+A browser is considered modern based on the [@searchspring/browserslist-config-snap modern](https://github.com/searchspring/browserslist-config-snap/blob/main/modern/index.js) rules and is included in the preconfigured scaffold.
+
+
+The universal build is used for projects targeting legacy browsers and will transpile any usage of modern JavaScript to ES5 as well as polyfill any missing browser features. If you are not targeting legacy browsers, you can omit deploying the universal built files that are prefixed with `universal.` - Example: `universal.bundle.js` and `universal.bundle.chunk.[hash].js`
+
+A browser is considered legacy based on the [@searchspring/browserslist-config-snap universal](https://github.com/searchspring/browserslist-config-snap/blob/main/universal/index.js) rules and is included in the preconfigured scaffold.
+
+However, if you are targeting legacy browsers, it is not recommended to always serve the universal build files to all browsers—including modern browsers—as this will impact Core Web Vitals and performance negatively.
+
+Therefore, you will require a method for switching the front-end script `src` to point to the appropriate version of the build files depending on whether the browser is modern or legacy. This can be done in many ways, including:
+
+- Server-side checking the userAgent and serving the appropriate version of the build files.
+- Front-end checking the userAgent and serving the appropriate version of the build files.
+- Lambda function serving the appropriate version of the build files based on the userAgent.
+
+The following is an example of a regex that would match the versions of the `browserlist-config-snap@1.0.7` rules:
+
+```js
+module.exports = /((CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS)[ +]+(14|(1[5-9]|[2-9]\d|\d{3,})|15|(1[6-9]|[2-9]\d|\d{3,}))[_.]\d+(?:[_.]\d+)?)|((?:Chrome).*OPR\/(77|(7[8-9]|[8-9]\d|\d{3,}))\.\d+\.\d+)|(Edge\/(91|(9[2-9]|\d{3,}))(?:\.\d+)?)|((Chromium|Chrome)\/(91|(9[2-9]|\d{3,}))\.\d+(?:\.\d+)?)|(Version\/(14|(1[5-9]|[2-9]\d|\d{3,})|15|(1[6-9]|[2-9]\d|\d{3,}))\.\d+(?:\.\d+)? Safari\/)|(Firefox\/(74|(7[5-9]|[8-9]\d|\d{3,}))\.\d+\.\d+)|(Firefox\/(74|(7[5-9]|[8-9]\d|\d{3,}))\.\d+(pre|[ab]\d+[a-z]*)?)/;
+```
+(regex generated using [browserslist-useragent-regexp](https://www.npmjs.com/package/browserslist-useragent-regexp))
+
+
+
+## Build Tools
+
+Webpack is the default choice of build tooling that all Snapfu templates include and will be preconfigured.
+
+If you are integrating `@searchspring/snap-preact` using other build tools, you may require certain plugins to ensure preact compatibility.
+
+We hope to maintain this page with the most common build tools and their respective plugins as we discover them.
+
+
+### Vite
+
+[@preact/preset-vite](https://github.com/preactjs/presets/tree/main/packages/preset-vite) is a plugin for Vite that allows you to use Preact in your Vite project.
+
+```js
+// vite.config.js
+import { defineConfig } from 'vite'
+import preact from '@preact/preset-vite'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [preact()],
+})
+```
\ No newline at end of file
diff --git a/docs/BUILD_DEPLOY_CHECKLIST.md b/docs/BUILD_DEPLOY_CHECKLIST.md
new file mode 100644
index 0000000000..204f3e4b97
--- /dev/null
+++ b/docs/BUILD_DEPLOY_CHECKLIST.md
@@ -0,0 +1,35 @@
+# QA Checklist
+
+## Integration Code Checklist
+
+- [Background Filters](./snap-background-filters) are being read from script context and applied as client globals to handle Category pages.
+
+- (If applicable) [Foreground Filters](./snap-background-filters) are added as client globals to handle custom filtering (ie. only show results in stock)
+
+- Merchandising [Banner](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-banner--footer) components have been added for all banner locations: `header`, `banner`, `footer`, `left`. See [Search > Search Store Merchandising](./snap-search#searchcontrollerstoremerchandising)
+
+- [InlineBanner](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-inlinebanner--default) component is conditionally rendered in your Results component. See [Search > Search Store Results](./snap-search#searchcontrollerstoreresults)
+
+- [Result impression tracking](./snap-tracking#impressions) has been added. Confirm via browser dev tools network tab that beacon impression events are being sent for results in Search, Category, Autocomplete, and Recommendations
+
+- [Result click tracking](./snap-tracking#product-click) has been added. (not required if using `withTracking` or `ResultTracker` as this functionallity is handled by those components)
+
+- [Result add to cart tracking](./snap-tracking#product-add-to-cart) has been added. Confirm via browser dev tools network tab that beacon add to cart events are being sent for results in Search, Category, Autocomplete, and Recommendations
+
+- (If enabled) Self-configured [Badges](./snap-badges) are added to your Result component
+
+- No Results component text has been personalized for your store (ie. contains support contact and storefront address information)
+
+## Platform Integration Checklist
+
+- [Shopper Login tracking](./snap-tracking#shopper-login) has been added. Confirm via browser dev tools network tab that beacon shopper login events are being sent only when a shopper is logged in
+
+- [Currency tracking](./snap-tracking#currency) has been added. Confirm via browser dev tools network tab that beacon events contain currency code in data payload for any event. If using a single currency, this is not required.
+
+- [Product view tracking](./snap-tracking#product-view). Confirm via browser dev tools that last viewed products cookie is being updated when a product is viewed.
+
+- [Order transaction tracking](./snap-tracking#order-transaction) has been added. Confirm via browser dev tools network tab that beacon order transaction events are being sent only when an order is completed.
+
+- [Cart contents tracking](./snap-tracking#cart-contents) has been added. Confirm via browser dev tools network tab that beacon cart add and remove events are being sent when cart contents change.
+
+- (If applicable) Realtime recommendations requires [Cart Attribute Tracking](./snap-tracking#cart-attribute-tracking)
diff --git a/docs/INTEGRATION_CONTEXT.md b/docs/BUILD_DEPLOY_INTEGRATION.md
similarity index 57%
rename from docs/INTEGRATION_CONTEXT.md
rename to docs/BUILD_DEPLOY_INTEGRATION.md
index a747c49a6f..4dce7a4046 100644
--- a/docs/INTEGRATION_CONTEXT.md
+++ b/docs/BUILD_DEPLOY_INTEGRATION.md
@@ -1,15 +1,48 @@
+# Integration
+
+After building the project and uploading the build files to your CDN or hosting provider, you will need to add a script tag to your storefront.
+
+
+```html
+
+```
+
+The bundle should be included in the tag, ideally near the top of the node, and should not have a 'defer' or 'async' attribute. This location is important in order to start fetching results and as soon as possible. This placement prior to any body elements also serves to allow for the hiding of targeted elements that contain content - this preventing a flash when the contents change upon injection.
+
+
+```html
+
+
+
+
+
+
+ Snap Integration Example
+
+
+
+
+
+
+
+```
+
## Context Variables
-Context variables are conditionally rendered within the `bundle.js` script's innerHTML via server side code or template logic. They provide various context variables that can be utilized by the Snap integration. Typically these variables are used to specify category page details (for [background filtering](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_BACKGROUND_FILTERS.md)), shopper details (for personalization), merchandising segmentation, or any other custom variables needed for the integration.
+Context variables are conditionally rendered within the `bundle.js` script's innerHTML via server side code or template logic. They provide various context variables that can be utilized by the Snap integration. Typically these variables are used to specify category page details (for [background filtering](https://searchspring.github.io/snap/snap-background-filters)), shopper details (for personalization), merchandising segmentation, or any other custom variables needed for the integration.
-The innerHTML of the script MUST only contain variable assignments without `var`, `let`, or `const`. Each declaration should end with a semi-colon to ensure minification does not impact the functions ability to parse the innerHTML. These variables are retrieved using the [getContext](https://github.com/searchspring/snap/tree/main/packages/snap-toolbox/src/getContext) function at run time.
+The innerHTML of the script MUST only contain variable assignments without `var`, `let`, or `const`. Each declaration should end with a semi-colon to ensure minification does not impact the functions ability to parse the innerHTML. These variables are retrieved using the [getContext](https://searchspring.github.io/snap/reference-toolbox-getcontext) function at run time.
There are a few core context variables utilized by Snap, `shopper`, `merchandising` and `config` - these are reserved context variable names and should not be utilized for custom context functionality.
| Option | Value | Page | Description |
|---|---|:---:|---|
-| shopper.id | logged in user unique identifier | all | required for personalization functionallity |
-| shopper.cart | array of cart objects, each object in the array can contain `uid` (required), `childUid`, `sku`, `childSku`, `price`, `qty` | all | current cart contents, required if checkout process does not contain a dedicated cart page (ie. slideout cart) |
+| shopper.id | logged in user unique identifier | all | required for personalization functionality |
+| shopper.cart | array of cart objects, each object in the array should contain `uid` (required), `parentId` (required), `sku`, `price`, `qty` | all | current cart contents, required if checkout process does not contain a dedicated cart page (ie. slideout cart) |
| currency.code | currency code string, ie. 'EUR' (ISO 4217) | all | currency code of the shopper's cart contents or order confirmation. Used for beacon events containing pricing data |
| merchandising.segments | array of strings used for merchandising | any | segmented merchandising allows for custom control over products returned on search requests and must also be setup within the Searchspring Management Console (SMC) |
| config | object containing Snap configurations | any | advanced usage of Snap (not recommended for standard integrations) |
@@ -20,7 +53,7 @@ There are a few core context variables utilized by Snap, `shopper`, `merchandisi
The custom variable example below shows a custom context being added for 'page'. The value would typically be assigned server side using template logic. This would be used to possibly toggle the siteId utilized by the client (to fetch different catalog data) or to modify text or currency displays.
```html
-
```
@@ -28,15 +61,14 @@ The custom variable example below shows a custom context being added for 'page'.
When used, shopper context should always include at least an `id`; the `cart` contents can optionally be provided to ensure personalization is applied on every page. Standard Snap integrations will automatically take this context data and apply it for personalization.
```html
-
-```
\ No newline at end of file
+```
+
+## Content Security Policy
+
+If your site requires a strict [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP), an entry of `https://*.searchspring.io` should be added to your CSP configuration to ensure Searchspring is functional.
+
diff --git a/docs/BUILD_DEPLOY_INTEGRATION_BIGCOMMERCE.md b/docs/BUILD_DEPLOY_INTEGRATION_BIGCOMMERCE.md
new file mode 100644
index 0000000000..951ae7e063
--- /dev/null
+++ b/docs/BUILD_DEPLOY_INTEGRATION_BIGCOMMERCE.md
@@ -0,0 +1,461 @@
+# BigCommerce Integration
+
+## Searchspring Management Console Actions
+
+### Update Field Settings
+
+On the [Field Settings page](https://manage.searchspring.net/management/field-settings/display-fields), make sure the following fields are updated:
+
+| Field | Type | Multi-Valued | Display |
+|---|---|:---:|:---:|
+| categories_hierarchy | Text | | ✓ |
+| brand | Text | No | ✓ |
+
+If settings are changed, perform an [Update Index](https://manage.searchspring.net/management/index/status).
+
+## Add IntelliSuggest Tracking
+
+See [IntelliSuggest Tracking for BigCommerce Stencil](https://searchspring.zendesk.com/hc/en-us/articles/360056186252-IntelliSuggest-Tracking-for-BigCommerce-Stencil).
+
+## Create a Category Search Page
+
+> [!WARNING]
+> Before creating this category page, ensure that the url does not already exist by going to `https://[domain]/shop`. If the url is active, alternative paths would be `search` or `ssearch`.
+
+- Products > Product Categories and click "Create a Category".
+- Set the category details to the following:
+
+| Option | Value |
+|---|---|
+| Name | Searchspring |
+| URL | /searchspring/ |
+| Parent Category | -- No Parent Category -- |
+| Template Layout File | Default (aka category.html) |
+| Search Engine Optimization > Page Title | (leave blank) |
+
+- On the "Product Categories" listing, set the "Searchspring" category to Visible in Menu > No ("x" icon).
+- On the "Product Categories" listing, click Actions > New Sub Category to the right of the "Searchspring" row.
+- Set the category details to the following:
+
+| Option | Value |
+|---|---|
+| Name | Search Results |
+| URL | /shop/ |
+| Parent Category | Searchspring |
+| Template Layout File | Default (aka category.html) |
+| Search Engine Optimization > Page Title | Search Results |
+
+- Leave the "shop" page as Visible in Menu > Yes ("check" icon).
+- You do not need to add products to these categories.
+- You can preview the category by going to `https://[domain]/shop`.
+
+> [!CAUTION]
+> If you are not seeing your category page, it may be because the page is locked down (by default) to certain customer groups. Go to Customers > Customer Groups > "..." under Actions > Edit > Group Details > Group Access.
+
+## Theme Integration
+
+Next we'll integrate Searchspring into the theme.
+
+- Create a copy of the current theme to integrate on. It is recommended to do so rather than integrating directly on the live theme initially to allow for testing prior to going live.
+- Storefront > My Themes > Live theme > Advanced > Make a Copy.
+- Rename the duplicated theme to something like "Theme Name - Searchspring".
+- Previewing an unpublished theme copy is not particularly easy with BigCommerce as there is no preview theme link. First, right click on "My Themes" in the left navigation and open this in a new tab. An extra tab is needed so you can go back through the BigCommerce admin and edit code. On either tab, next to the copy of your theme there should be an icon that looks like three dots "...". Click this then "Customize". If asked which theme style you want to view, pick the option that looks like the site (typically the first) and "Continue". This tab will now be used to preview the theme.
+
+### Base.html Edits
+
+- Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > layout > base.html.
+- Before the closing `` tag, add the integration snippet.
+
+#### Search Only
+
+> [!NOTE]
+> If you are only integrating Search page functionality, you can use the following snippet. Otherwise skip this section and continue below to install both search, category, and brand functionality.
+
+- Update `REPLACE_WITH_YOUR_SITE_ID` with the correct site id.
+- If your search page url was not `/shop/`, update this as needed.
+
+```handlebars
+{{!-- START: Searchspring Integration code --}}
+
+{{!-- define initial variables --}}
+{{assignVar 'ss_site_id' 'REPLACE_WITH_YOUR_SITE_ID'}}
+{{assignVar 'ss_search_url' '/shop/'}}
+{{assignVar 'ss_page_type' 'other'}}
+
+{{!-- check if on search page --}}
+{{#if category}}
+ {{#contains category.url (getVar 'ss_search_url')}}{{assignVar 'ss_page_type' 'search'}}{{else}}{{assignVar 'ss_page_type' 'category'}}{{/contains}}
+{{else if brand}}
+ {{assignVar 'ss_page_type' 'brand'}}
+{{/if}}
+
+{{!-- create integration script --}}
+
+
+{{!-- END: Searchspring Integration code --}}
+```
+
+#### Search, Category, and Brand
+
+- Update `REPLACE_WITH_YOUR_SITE_ID` with the correct site id.
+- If your search page url was not `/shop/`, update this as needed.
+- Replace the `>` character in the breadcrumb trail if the data is using a different delimiter.
+
+```handlebars
+{{!-- START: Searchspring Integration code --}}
+
+{{!-- define initial variables --}}
+{{assignVar 'ss_site_id' 'REPLACE_WITH_YOUR_SITE_ID'}}
+{{assignVar 'ss_search_url' '/shop/'}}
+{{assignVar 'ss_page_type' 'other'}}
+{{assignVar 'ss_is_loaded' 'false'}}
+
+{{!-- check if results should load --}}
+{{#or category brand}}
+ {{assignVar 'ss_is_loaded' 'true'}}
+{{/or}}
+
+{{!-- check if on search page --}}
+{{#if category}}
+ {{#contains category.url (getVar 'ss_search_url')}}{{assignVar 'ss_page_type' 'search'}}{{else}}{{assignVar 'ss_page_type' 'category'}}{{/contains}}
+{{else if brand}}
+ {{assignVar 'ss_page_type' 'brand'}}
+{{/if}}
+
+{{!-- create integration script --}}
+
+
+{{!-- END: Searchspring Integration code --}}
+```
+
+### Body Class Name
+
+(Optional) There may be occasions where you need to add a class name to the `body` element on the search page for styling purposes.
+
+If `body` tag has no `class` attribute:
+
+```handlebars
+
+```
+
+If `body` tag has a `class` attribute, ensure to keep the existing class names and append the `ss-shop` class name to the existing list of class names:
+
+```handlebars
+
+```
+
+## Category and Brand Page Edits
+
+Next we'll add our target element(s) to the category and brand pages. This is where the Searchspring elements will be injected into, typically two elements are added for a two-column layout: one for content, and one for facets.
+
+Targets are defined in your Snap configuration and will only be injected into if they exist on the page.
+
+### Category Page Edits
+
+- Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > pages > category.html.
+- `category.html` is a standard BigCommerce template, but this may not be the file to edit. Look for includes which will tell you where to go, for example: `{{> thing/parent/child}}`. This says that there's additional code for the category page located in another file that has the name "child".
+- Once the correct file is found, ensure that all of your search controller targets are added to the category template. Use theme comments to hide the store's default product grid, thus speeding up load time for Searchspring.
+
+```handlebars
+
+{{!--
+
+--}}
+
+
+{{!--
+
+--}}
+```
+
+- Sidebar elements may be within a separate file. Typically this is found in Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > components > category > sidebar.html, which is the sidebar layout specifically for category pages.
+- Some category templates will only display sidebar or results content if products are assigned to the category. You can make these elements show on the category search page with additional checks where required.
+
+Show an element only when on the `shop` category:
+
+- If your search page url was not `/shop/`, update this as needed.
+
+```handlebars
+{{#contains category.url '/shop/'}}
+
+{{/contains}}
+```
+
+### Brand Page Edits
+
+- Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > pages > brand.html.
+- Make sure you are within `brand.html` as `brands.html` file contains the listing of all brands.
+- `brand.html` is a standard BigCommerce template, but this may not be the file to edit. Look for includes which will tell you where to go, for example: `{{> thing/parent/child}}`. This says that there's additional code for the brand page located in another file that has the name "child".
+- Once the correct file is found, ensure that all of your search controller targets are added to the brand template. Use theme comments to hide the store's default product grid, thus speeding up load time for Searchspring.
+
+```handlebars
+
+{{!--
+
+--}}
+
+
+{{!--
+
+--}}
+```
+
+- Sidebar elements may be within a separate file. Typically this is found in Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > components > brand > sidebar.html, which is the sidebar layout specifically for brand pages.
+
+## Search Form Updates
+
+Next we'll update the search form to submit to the search results category page (ie. `/shop/`) we created.
+
+- Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > components > common > header.html.
+- If you do not see the search form in the header, it might be in another snippet. For example: Often in the header file you'll find the following line of code `{{> components/common/quick-search}}`. This means the search form is in quick-search.html.
+- Alternate locations:
+ - Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > components > common > quick-search.html
+ - Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > components > common > search-box.html
+- Create a copy of the form which you will edit and comment out the old form. This will allow us to retain BigCommerce's default functionality.
+
+```handlebars
+{{!--
+
+--}}
+
+```
+
+In your form copy, update the following details:
+
+| Element | Attribute | Value |
+|---|---|---|
+| form | method | get |
+| form | action | /shop/ |
+| input | | remove any `data-search-quick` attribute |
+| input[type="hidden"] | | remove any hidden inputs |
+| div | | remove any `quickSearchResults` div |
+
+## Integration Code
+
+Up until this point, we've added the Searchspring integration to the theme and category/brand pages.
+
+Now we'll ensure our integration code captures the context variables and sets up the necessary configuration.
+
+### Check Shopper and Site ID Code
+
+For tracking shopper data used in personalization features, we need to include code in `src/index.js`. Shopper code should be in `index.js` by default, but if not, follow the below steps. `siteId` context should also be included, but adding it will allow for the implementation of multi-site integrations later.
+
+```js
+// src/index.js
+/* searchspring imports */
+import { Snap } from '@searchspring/snap-preact';
+import { getContext } from '@searchspring/snap-toolbox';
+
+/* context from script tag */
+const context = getContext(['shopper', 'siteId']);
+```
+
+### Add Site and Page Objects
+
+Still within `index.js`, add objects to setup a `site` and `page` config. This may already be present depending on what Snap templates you are using.
+
+> [!NOTE]
+> Update `REPLACE_WITH_YOUR_SITE_ID` with the correct, default site id.
+
+> [!NOTE]
+> If the default query parameter is something other than "search_query", update the parameter.
+
+> [!WARNING]
+> Due to certain issues with BigCommerce integrations, these sites should always change the pagination parameter to not match the default. "p" as the page parameter is usually our default for this platform.
+
+```js
+// src/index.js
+
+/* set up site details config */
+let site = {
+ id: context?.siteId ? context.siteId : 'REPLACE_WITH_YOUR_SITE_ID',
+ currency: 'usd',
+ lang: 'en',
+ parameters: {
+ query: 'search_query',
+ page: 'p',
+ },
+ features: {
+ integratedSpellCorrection: {
+ enabled: true,
+ },
+ },
+};
+
+/* set up page details config */
+let isSearch = window.location.href.includes('/shop') || window.location.href.includes('/mockup') || window.location.href.includes('/lighthouse') ? true : false;
+let page = {
+ id: isSearch ? 'shop' : 'other',
+ title: isSearch ? 'Search Results' : 'Other Page',
+ type: isSearch ? 'search' : 'other',
+};
+```
+
+Pass these configs into a plugin and set on `store.custom`:
+
+```js
+// src/plugins/sharedPlugin.js
+export const sharedPlugin = (controller, site, page) => {
+ // set initial custom settings for project
+ controller.store.custom = { ...controller.store.custom, site: site, page: page };
+};
+```
+
+For a fuller example, please see the template repo [here](https://github.com/searchspring/snapfu-template-preact-implementations/tree/production/src).
+
+### Category and Brand Integration (Optional)
+
+If integrating on category and brand pages, follow these steps. If not, skip this section.
+
+Back in `src/index.js`, adjust context to grab additional values for category and brand integration.
+
+```js
+// src/index.js
+/* context from script tag */
+const context = getContext(['category', 'brand', 'shopper', 'siteId']);
+```
+
+Below this, add code to support background filters on a `search` controller. As a best practice, for category pages we use `categories_hierarchy` as it contains a unique, hierarchal path for the category. On brand, the `brand` data should contain the brand name.
+
+```js
+// src/index.js
+
+/* background filters */
+let backgroundFilters = [];
+
+if (context.category?.path) {
+ const categoryPath = replaceCharacters(context.category.path);
+ const categoryName = replaceCharacters(context.category.name);
+
+ // update page details when on category
+ page = {
+ id: categoryPath,
+ title: categoryName,
+ type: 'category',
+ };
+
+ // set category background filter
+ backgroundFilters.push({
+ field: 'categories_hierarchy',
+ value: categoryPath,
+ type: 'value',
+ background: true,
+ });
+} else if (context.brand?.name) {
+ const brandName = replaceCharacters(context.brand.name);
+
+ // update page details when on brand
+ page = {
+ id: brandName,
+ title: brandName,
+ type: 'brand',
+ };
+
+ // set brand background filter
+ backgroundFilters.push({
+ field: 'brand',
+ value: brandName,
+ type: 'value',
+ background: true,
+ });
+}
+
+/* replace special characters in values */
+function replaceCharacters(value) {
+ if (value) {
+ return value.replace(/\&\;/g, '&').replace(/\<\;/g, '<').replace(/\>\;/g, '>').replace(/\"\;/g, '"').replace(/\'\;/g, "'").replace(/\'\;/g, "'").trim();
+ } else {
+ return '';
+ }
+}
+```
+
+To attach the `backgroundFilters` to a `search` controller, they need to be added into a `globals` config.
+
+```js
+// src/index.js
+import { sharedPlugin } from './plugins/sharedPlugin';
+const snap = new Snap({
+ client: {
+ globals: {
+ siteId: 'REPLACE_WITH_YOUR_SITE_ID',
+ },
+ },
+ controllers: {
+ search: [
+ {
+ config: {
+ id: 'search',
+ plugins: [[sharedPlugin, site, page], [contentPlugin]],
+ settings: {
+ redirects: {
+ singleResult: true,
+ },
+ facets: {
+ pinFiltered: true,
+ },
+ },
+ globals: {
+ filters: backgroundFilters,
+ },
+ },
+ targeters: [
+ {
+ selector: '#searchspring-content',
+ component: async () => {
+ return (await import('./components/Content/Content')).Content;
+ },
+ },
+ ],
+ },
+ ],
+ },
+});
+```
+
+> [!NOTE]
+> Typically, you should complete the brand integration even if brand pages are not visible. This gives the client the option to show brand integration later. You can find brand pages by going to `https://[domain]/brands` then click on one of the brands in the listing.
+
+## Additional Targets (Optional)
+
+In addition to having two targets for a two-column layout, you may want to inject content into other sections of the page such above the content and sidebar to display information such as the search query. Note the addition of `ss-shop` class before `searchspring-header` to ensure the content is only injected on the search page.
+
+```js
+// src/index.js
+targeters: [
+ {
+ selector: '#searchspring-content',
+ component: async () => {
+ return (await import('./content/content/Content')).Content;
+ },
+ hideTarget: true,
+ },
+ {
+ selector: '#searchspring-sidebar',
+ component: async () => {
+ return (await import('./sidebar/sidebar/Sidebar')).Sidebar;
+ },
+ hideTarget: true,
+ },
+ {
+ selector: '.ss-shop #searchspring-header',
+ component: async () => {
+ return (await import('./content/header/Header')).Header;
+ },
+ hideTarget: true,
+ },
+],
+```
diff --git a/docs/BUILD_DEPLOY_INTEGRATION_MAGENTO2.md b/docs/BUILD_DEPLOY_INTEGRATION_MAGENTO2.md
new file mode 100644
index 0000000000..19a7570d1a
--- /dev/null
+++ b/docs/BUILD_DEPLOY_INTEGRATION_MAGENTO2.md
@@ -0,0 +1,520 @@
+# Magento2 Integration
+
+## Searchspring Management Console Actions
+
+### Update Field Settings
+
+On the [Field Settings page](https://manage.searchspring.net/management/field-settings/display-fields), make sure the following fields are updated:
+
+**For Search Only Integration:**
+
+| Field | Type | Multi-Valued | Display |
+|---|---|:---:|:---:|
+| visibility | Text | , | ✓ |
+
+**For Search and Category Integration:**
+
+| Field | Type | Multi-Valued | Display |
+|---|---|:---:|:---:|
+| visibility | Text | , | ✓ |
+| category_hierarchy | Text | \| | ✓ |
+
+If settings are changed, perform an [Update Index](https://manage.searchspring.net/management/index/status).
+
+## Add IntelliSuggest Tracking
+
+Have the client install the following IntelliSuggest tracking module: [github.com/SearchSpring/magento2-searchspring-tracking](https://github.com/SearchSpring/magento2-searchspring-tracking).
+
+Notify the client which site id to use for the section "How to Install", step 4, point iii.
+
+## Create a Category Search Page
+
+> [!WARNING]
+> Before creating this category page, ensure that the url does not already exist by going to `https://[domain]/shop.html`. If the url is active, alternative paths would be `search` or `ssearch`.
+
+> [!NOTE]
+> If this is a re-mockup (and sometimes even a re-platform), it is recommended to keep the same search page url (and query parameter) so customers do not need to create redirects. If the site was previously integrated using CMS page rather than a category search page, you should be able to proceed with the below methods. This is because CMS and category page urls can be formatted the same way.
+
+- Magento 2 Admin > Left Navigation > Catalog > Categories > Add Subcategory.
+- The category should be created wherever most categories are in the current view, which means usually you wouldn't use the "Add Root Category" button. Set the details to the following:
+
+| Section | Option | Value |
+|---|---|---|
+| Currently Active | Enable Category | Yes |
+| Include in Menu | No | |
+| Category Name | Search Results | |
+| Search Engine Optimization | Url Key | shop |
+| Search Engine Optimization | Meta Title | Search Results |
+| Design | Layout | 2 columns with left bar or 2 columns with right bar (but could be full width) |
+
+- You do not need to add products to this category.
+- You can preview the category by going to `https://[domain]/shop.html`.
+- Go back to Magento 2 Admin > Left Navigation > Catalog > Categories. Click on the search category link to bring up the details. At the top of the page, it should say "Search Results (ID: 000000)". Note the id number as we will use it later.
+
+## Theme Integration
+
+Next we'll integrate Searchspring into the theme.
+
+> [!NOTE]
+> If possible, have the site owner enable Template Path Hints. This will make it easier to find which files need to be changed.
+
+### Root.phtml Edits
+
+- FTP > app > design > frontend > [theme vendor] > [theme name] > Magento_Theme > templates > root.phtml.
+
+> [!CAUTION]
+> If this file doesn't exist, find the root file of the same, make a copy, and upload it to the path above. Try looking in: vendor > magento > magento-theme > view > frontend > templates > root.phtml. Do not edit the root files directly because if the client updates Magento, this could erase the changes and break the integration.
+
+- Add the below code at the end of the file.
+
+#### Search Only
+
+> [!NOTE]
+> If you are only integrating Search page functionality, you can use the following snippet. Otherwise skip this section and continue below to install both search and category functionality.
+
+- Replace `REPLACE_WITH_YOUR_SITE_ID` with the correct site id.
+- Replace `000000` with the search category id that was noted when creating the category page.
+
+```php
+get('Magento\Framework\Registry')->registry('current_category');
+
+ // Update defer if on search category
+ if (isset($ss_category) && $ss_category->getId() == 000000) {
+ $ss_defer_config = '';
+ }
+?>
+
+
+
+```
+
+#### Search and Category
+
+- Replace `REPLACE_WITH_YOUR_SITE_ID` with the correct site id.
+- Replace `000000` with the search category id that was noted when creating the category page.
+- (Optional) If the category path attribute has a parent level you want to exclude, add to the `$ss_category_exclude` array.
+
+```php
+get('Magento\Framework\Registry')->registry('current_category');
+
+ // Update details if on category
+ if (isset($ss_category)) {
+ $ss_category_id = $ss_category->getId();
+ $ss_category_name = str_replace($ss_find, $ss_replace, $ss_category->getName());
+ $ss_category_copy = $ss_category;
+ $ss_category_array = array();
+ $ss_category_exclude = array('Default Category', 'Root Catalog');
+
+ // Build category path and trim characters
+ while (!in_array($ss_category_copy->getName(), $ss_category_exclude)) {
+ $ss_category_array[] = trim(htmlspecialchars($ss_category_copy->getName()));
+ $ss_category_copy = $ss_category_copy->getParentCategory();
+ }
+ $ss_category_join = implode('>', array_reverse($ss_category_array));
+ $ss_category_path = str_replace($ss_find, $ss_replace, $ss_category_join);
+
+ // Update defer if on category
+ $ss_defer_config = '';
+
+ // Add category filter (but not on search category)
+ if ($ss_category_id != 000000) {
+ $ss_category_config = 'category = { id : "' . $ss_category_id . '", name : "' . $ss_category_name . '", path : "' . $ss_category_path . '" };';
+ }
+ }
+?>
+
+
+
+```
+
+### Body Class Name
+
+Magento automatically adds a class based on the name of the page to the body, so there is no additional code needed to generate `category-shop` class. This class can be used for styling purposes or to target elements specifically on the search page.
+
+## Category Page Edits
+
+Next we'll add our target element(s) to the category page. This is where the Searchspring elements will be injected into, typically two elements are added for a two-column layout: one for content, and one for facets.
+
+Targets are defined in your Snap configuration and will only be injected into if they exist on the page.
+
+### Category Listing
+
+- FTP > app > design > frontend > [theme vendor] > [theme name] > Magento_Catalog > templates > product > list.phtml.
+
+> [!CAUTION]
+> If this file doesn't exist, find the root file of the same, make a copy, and upload it to the path above. Try looking in: vendor > magento > magento-catalog > view > frontend > templates > list.phtml. Do not edit the root files directly because if the client updates Magento, this could erase the changes and break the integration.
+
+- Once the correct file is found, ensure that all of your search controller targets are added to the category listing.
+- Some category templates will only display sidebar or results content if products are assigned to the category. Look for conditions that may be checking for product count and adjust as needed (likely altering the product count conditional).
+
+```php
+
+```
+
+### Category Sidebar
+
+- FTP > app > design > frontend > [theme vendor] > [theme name] > Magento_LayeredNavigation > templates > layer > view.phtml.
+
+> [!CAUTION]
+> If this file doesn't exist, find the root file of the same, make a copy, and upload it to the path above. Try looking in: vendor > magento > magento-layerednavigation > view > frontend > templates > view.phtml. Do not edit the root files directly because if the client updates Magento, this could erase the changes and break the integration.
+
+- Once the correct file is found, ensure that all of your search controller targets are added to the category sidebar.
+
+```php
+
+```
+
+## Search Form Updates
+
+Next we'll update the search form to submit to the search results category page (ie. `/shop.html`) we created.
+
+- FTP > app > design > frontend > [theme vendor] > [theme name] > Magento_Search > templates > form.mini.phtml.
+
+> [!CAUTION]
+> If this file doesn't exist, find the root file of the same, make a copy, and upload it to the path above. Try looking in: vendor > magento > magento-search > view > frontend > templates > form.mini.phtml. Do not edit the root files directly because if the client updates Magento, this could erase the changes and break the integration.
+
+- Above the form, add a php flag to enable and disable Searchspring:
+
+```php
+
+```
+
+- Create a copy of the form which you will edit and comment out the old form. This will allow us to retain Magento's default functionality for when Searchspring is disabled.
+
+```php
+
+
+
+
+
+```
+
+In your form copy, update the following details:
+
+| Element | Attribute | Value |
+|---|---|---|
+| form | method | get |
+| form | action | /shop.html |
+| input[type="text"] or input[type="search"] | data-mage-init | remove attribute |
+| input[type="hidden"] | | remove any hidden inputs |
+| search_autocomplete | | remove element |
+
+## Integration Code
+
+Up until this point, we've added the Searchspring integration to the theme and category page.
+
+Now we'll ensure our integration code captures the context variables and sets up the necessary configuration.
+
+### Create Magento2 Plugin
+
+In the `src/scripts` folder, create a plugin called `magento2.js` and add the below code. This snippet will provide tracking for shopper id; setting form key and uenc; and a few common pieces of Magento 2 logic used in results display.
+
+```js
+// src/scripts/magento2.js
+import { cookies } from '@searchspring/snap-toolbox';
+
+export const magento2 = (controller) => {
+ // get shopper id from magento cache
+ const mageCacheStorage = JSON.parse(localStorage.getItem('mage-cache-storage'));
+ let shopperId = (mageCacheStorage?.customer?.data_id) ? mageCacheStorage.customer.data_id : false;
+
+ // track shopperId
+ if (shopperId) {
+ controller.tracker.track.shopper.login({
+ id: shopperId
+ });
+ }
+
+ // magento2 configs
+ controller.store.custom.m2 = {
+ domain: window.location.hostname,
+ formKey: cookies.get('form_key'),
+ uenc: typeof btoa == 'function' ? btoa(window.location.href) : '',
+ };
+
+ controller.on('afterStore', async ({ controller }, next) => {
+ const store = controller.store;
+ const { pagination, results } = store;
+ const customM2 = store.custom.m2;
+ customM2.uenc = typeof btoa == 'function' ? btoa(window.location.href) : '';
+
+ if (pagination.totalResults) {
+ results.forEach((result) => {
+ const core = result.mappings.core;
+ const custom = result.custom;
+
+ // shared magento2 action config
+ const sharedData = {
+ data: {
+ product: core.uid,
+ uenc: customM2.uenc,
+ },
+ };
+
+ // magento2 wishlist action
+ let wishlistData = sharedData;
+ wishlistData.action = '//' + customM2.domain + '/wishlist/index/add/';
+ custom.wishlist = JSON.stringify(wishlistData).replace(/\//g, '\\/');
+
+ // magento2 compare action
+ let compareData = sharedData;
+ compareData.action = '//' + customM2.domain + '/catalog/product_compare/add/';
+ custom.compare = JSON.stringify(compareData).replace(/\//g, '\\/');
+
+ // magento2 Add to Cart action
+ custom.addToCart = '//' + customM2.domain + '/checkout/cart/add/uenc/' + customM2.uenc + '/product/' + core.uid + '/';
+ });
+ }
+
+ await next();
+ });
+};
+```
+
+### Search Only Integration
+
+If you are only integrating Search page functionality, follow these steps. Otherwise skip this section and continue below to install both search and category functionality.
+
+Add code to set a visibility filter on search pages:
+
+```js
+// src/index.js
+
+/* visibility filters */
+let visibilityFilters = [];
+visibilityFilters.push({
+ field: 'visibility',
+ value: 'Search',
+ type: 'value',
+ background: true,
+});
+```
+
+In the search controller config object, add the magento2 plugin and filters:
+
+```js
+// src/index.js
+import { magento2 } from './scripts/magento2';
+const snap = new Snap({
+ client: {
+ globals: {
+ siteId: 'REPLACE_WITH_YOUR_SITE_ID',
+ },
+ },
+ controllers: {
+ search: [
+ {
+ config: {
+ id: 'search',
+ plugins: [[magento2]],
+ settings: {
+ redirects: {
+ singleResult: true,
+ },
+ },
+ globals: {
+ filters: visibilityFilters,
+ },
+ },
+ targeters: [
+ {
+ selector: '#searchspring-content',
+ component: async () => {
+ return (await import('./components/Content')).Content;
+ },
+ },
+ ],
+ },
+ ],
+ autocomplete: [
+ {
+ config: {
+ id: 'autocomplete',
+ plugins: [[magento2]],
+ globals: {
+ filters: visibilityFilters,
+ search: {
+ query: {
+ spellCorrection: true,
+ },
+ },
+ pagination: {
+ pageSize: 6,
+ },
+ },
+ },
+ },
+ ],
+ },
+});
+```
+
+### Search and Category Integration
+
+If integrating on search and category pages, follow these steps.
+
+Back in `src/index.js`, adjust context to grab additional values for category integration:
+
+```js
+// src/index.js
+/* context from script tag */
+import { getContext } from '@searchspring/snap-toolbox';
+const context = getContext(['category']);
+```
+
+Below this, add code to support background filters on a `search` controller. As a best practice, we use `category_hierarchy` as it contains a unique, hierarchal path for the category.
+
+> [!CAUTION]
+> The background filter will almost always be `category_hierarchy`, but there are times when we create a custom field such as `ss_category` or `ss_category_hierarchy`. The background filter should match the category facet if there is one set. If you do need to update, be sure to change the below code to set the background filter on that field instead.
+
+```js
+// src/index.js
+
+/* background filters */
+let backgroundFilters = [];
+let visibilityFilters = [];
+
+if (context.category?.path) {
+ // set background filter
+ backgroundFilters.push({
+ field: 'category_hierarchy',
+ value: context.category.path.replace(/\"\;/g, '"'),
+ type: 'value',
+ background: true,
+ });
+
+ // add visibility filters for category
+ visibilityFilters.push({
+ field: 'visibility',
+ value: 'Catalog',
+ type: 'value',
+ background: true,
+ });
+} else {
+ // add visibility filters for search
+ visibilityFilters.push({
+ field: 'visibility',
+ value: 'Search',
+ type: 'value',
+ background: true,
+ });
+}
+```
+
+To attach the `backgroundFilters` and `visibilityFilters` to a `search` controller, they need to be added into a `globals` config:
+
+```js
+// src/index.js
+import { magento2 } from './scripts/magento2';
+const snap = new Snap({
+ client: {
+ globals: {
+ siteId: 'REPLACE_WITH_YOUR_SITE_ID',
+ },
+ },
+ controllers: {
+ search: [
+ {
+ config: {
+ id: 'search',
+ plugins: [[magento2]],
+ settings: {
+ redirects: {
+ singleResult: true,
+ },
+ },
+ globals: {
+ filters: visibilityFilters.concat(backgroundFilters),
+ },
+ },
+ targeters: [
+ {
+ selector: '#searchspring-content',
+ component: async () => {
+ return (await import('./components/Content')).Content;
+ },
+ },
+ ],
+ },
+ ],
+ autocomplete: [
+ {
+ config: {
+ id: 'autocomplete',
+ plugins: [[magento2]],
+ globals: {
+ filters: visibilityFilters,
+ search: {
+ query: {
+ spellCorrection: true,
+ },
+ },
+ pagination: {
+ pageSize: 6,
+ },
+ },
+ },
+ },
+ ],
+ },
+});
+```
+
+> [!NOTE]
+> In the above code, we concat `visibilityFilters` and `backgroundFilters` to the search controller. The autocomplete controller only needs a background filter on visibility.
+
+## Additional Targets (Optional)
+
+In addition to having two targets for a two-column layout, you may want to inject content into other sections of the page such above the content and sidebar to display information such as the search query. Note the addition of `category-shop` class before `searchspring-header` to ensure the content is only injected on the search page.
+
+```js
+// src/index.js
+targeters: [
+ {
+ selector: '#searchspring-content',
+ component: async () => {
+ return (await import('./components/Content')).Content;
+ },
+ hideTarget: true,
+ },
+ {
+ selector: '#searchspring-sidebar',
+ component: async () => {
+ return (await import('./components/Sidebar')).Sidebar;
+ },
+ hideTarget: true,
+ },
+ {
+ selector: '.category-shop #searchspring-header',
+ component: async () => {
+ return (await import('./content/header/Header')).Header;
+ },
+ hideTarget: true,
+ },
+],
+```
diff --git a/docs/BUILD_DEPLOY_INTEGRATION_SHOPIFY.md b/docs/BUILD_DEPLOY_INTEGRATION_SHOPIFY.md
new file mode 100644
index 0000000000..66635cac0a
--- /dev/null
+++ b/docs/BUILD_DEPLOY_INTEGRATION_SHOPIFY.md
@@ -0,0 +1,465 @@
+# Shopify Integration
+
+
+## Searchspring Management Console Actions
+
+Log into the Searchspring Management Console (SMC) and perform the following actions:
+
+### Add IntelliSuggest Tracking
+
+Shopify sites now add IntelliSuggest tracking through "Web Pixel Tracking". In the SMC, check the [Data Feed page](https://manage.searchspring.net/management/data-feed) and ensure this feature is enabled. If it's not, speak with the Solutions team member who setup the account.
+
+### Update Field Settings
+
+On the [Field Settings page](https://manage.searchspring.net/management/field-settings/display-fields), make sure the following fields are updated:
+
+| Field | Type | Multi-Valued | Display | Display In Recs |
+|---|---|:---:|:---:|:---:|
+| collection_handle | Text | | ✓ | |
+| handle | Text | No | ✓ | ✓ |
+| tags | Text | , | ✓ | |
+| vendor | Text | No | ✓ | |
+| product_type | Text | No | ✓ | |
+
+If settings are changed, perform an [Update Index](https://manage.searchspring.net/management/index/status).
+
+### "All" Collection Page (Optional)
+
+Most if not all Shopify sites have an "All" collection page located at `[domain]/collections/all`. Typically, products are automatically assigned to this collection and then fed back into our data feed. There have been occasions where this is not true and we don't have the `collection_handle` > `all` assignment in our data feed. If this happens, please inquire with Searchspring support to have this added.
+
+## Create a Collection Search Page
+
+To add a search results page, we'll need to create a new collection in Shopify.
+
+> [!WARNING]
+> Before creating this collection page, ensure that the url does not already exist by going to `https://[domain]/collections/shop`. If the url is active, an alternative path could be `search`.
+
+- Within the Shopify Admin, navigate to Products > Collections > Create collection.
+- Set the collection details to the following:
+
+| Option | Value |
+|---|---|
+| Title | Shop |
+| Collection type | Manual |
+
+- You do not need to add products to this collection.
+- The initial title value determines the collection handle so starting with "Shop" ensures the url is `/collections/shop`. If you need to use another url path, your "Title" should be different to format the correct collection handle.
+- After saving, edit this collection once more and change the "Title" to "Search Results".
+- You can preview the collection by going to `https://[domain]/collections/shop`.
+
+
+## Theme Integration
+
+Next we'll integrate Searchspring into the theme.
+
+- Create a copy of the current theme to integrate on. It is recommended to do so rather than integrating directly on the live theme initially to allow for testing prior to going live.
+- Online Store > Themes > Live theme > ... > Duplicate
+- To grab a link to the copied theme, click Online Store > Themes > [theme name] > ... > right click Preview > and Copy Link Address.
+
+### Theme Settings
+
+Next, we'll add the Searchspring theme settings. Theme settings are defined to provide an interface for defining variables that are used in liquid code. See [Shopify Setting Types](https://shopify.dev/themes/architecture/settings#setting-types) for more information. These theme settings are then accessed by going to Online Store > Themes > [theme name] > Customize button. Click on "Theme settings" in the lower left navigation and on the right side, there should be a "Searchspring" section.
+
+- Online Store > Themes > [theme name] > ... > Edit code > Config > `settings_schema.json`.
+- At the end of the file, find a closing square bracket `]` on the last line.
+- Before this closing bracket, add code for Searchspring theme settings. The opening comma is needed if there is another configuration before your code paste.
+
+```json
+{
+ "name": "Searchspring",
+ "settings": [
+ {
+ "type": "checkbox",
+ "id": "ss_enable",
+ "label": "Enable Searchspring",
+ "default": true
+ },
+ {
+ "type": "text",
+ "id": "ss_site_id",
+ "label": "Site ID",
+ "default": "REPLACE_WITH_YOUR_SITE_ID"
+ },
+ {
+ "type": "text",
+ "id": "ss_collection_handle",
+ "label": "Search collection handle",
+ "default": "shop"
+ },
+ {
+ "type": "text",
+ "id": "ss_branch_name",
+ "label": "Branch Name"
+ }
+ ]
+}
+```
+
+- Replace `REPLACE_WITH_YOUR_SITE_ID` on line 15 with the correct siteId found in the Searchspring Management Console.
+- (if applicable) Replace `shop` on line 21 with the search collection handle if it was not `shop`.
+
+
+## Create a Liquid Template Snippet
+
+Liquid template snippets are used to store code that is used in multiple templates.
+
+Create a new snippet which will be used to store the Searchspring integration script code.
+
+- Online Store > Themes > [theme name] > ... > Edit code > Snippets > Add a new snippet > `ss-script`.
+
+### Search Only
+
+> [!NOTE]
+> If you are only integrating Search page functionality, you can use the following snippet. Otherwise skip this section and continue below to install both search and collections functionality.
+
+```liquid
+{%- if settings.ss_branch_name != blank -%}
+ {% capture ss_branch_name %}/{{ settings.ss_branch_name }}{% endcapture %}
+{%- endif -%}
+
+{%- if customer -%}
+ {% capture ss_shopper_config %}
+ shopper = { id: "{{ customer.id }}" };
+ {% endcapture %}
+{%- endif -%}
+
+{% assign ss_defer_config = ' defer' %}
+{%- if collection.handle and template contains 'collection' and collection.handle == settings.ss_collection_handle -%}
+ {% assign ss_defer_config = '' %}
+{%- endif -%}
+
+{%- if template -%}
+ {% capture ss_template_config %}
+ template = "{{ template }}";
+ {% endcapture -%}
+{%- endif -%}
+
+{% capture ss_money_config %}
+ format = "{{ shop.money_format }}";
+{% endcapture %}
+
+{% comment %}Searchspring Script{% endcomment %}
+
+```
+
+### Search and Collections
+
+```liquid
+{%- if settings.ss_branch_name != blank -%}
+ {% capture ss_branch_name %}/{{ settings.ss_branch_name }}{% endcapture %}
+{%- endif -%}
+
+{%- if customer -%}
+ {% capture ss_shopper_config %}
+ shopper = { id: "{{ customer.id }}" };
+ {% endcapture %}
+{%- endif -%}
+
+{% assign ss_defer_config = ' defer' %}
+{%- if collection.handle and template contains 'collection' -%}
+ {% assign ss_defer_config = '' %}
+ {%- if collection.handle != settings.ss_collection_handle -%}
+ {% capture ss_collection_config %}
+ collection = { id: "{{ collection.id }}", name: "{{ collection.title | replace: '"', '"' }}", handle: "{{ collection.handle }}" };
+ {% endcapture %}
+ {%- endif -%}
+{%- endif -%}
+
+{%- if current_tags -%}
+ {% capture ss_tags_config %}
+ tags = {{ current_tags | json }};
+ {% endcapture %}
+{%- endif -%}
+
+{%- if template -%}
+ {% capture ss_template_config %}
+ template = "{{ template }}";
+ {% endcapture -%}
+{%- endif -%}
+
+{% capture ss_money_config %}
+ format = "{{ shop.money_format }}";
+{% endcapture %}
+
+{% comment %}Searchspring Script{% endcomment %}
+
+```
+
+### Snippet Installation
+
+Next, we'll integrate the `ss-script` snippet into the theme. We'll have to add the snippet to the theme.liquid file such that it is included on every page.
+It is recommended to install the snippet in the `head` tag so that the script is loaded as soon as possible.
+
+- Online Store > Themes > [theme name] > ... > Edit code > Layout > `theme.liquid`.
+- Before the closing `` tag, add the following code:
+
+```liquid
+{% if settings.ss_enable %}
+ {% render 'ss-script' %}
+{% endif %}
+```
+
+### Body class name
+
+(Optional) There may be occasions where you need to add a class name to the `body` element on the search page for styling purposes.
+
+If `body` tag has no `class` attribute:
+
+```liquid
+
+```
+
+If `body` tag has a `class` attribute, ensure to keep the existing class names and append the `ss-shop` class name to the existing list of class names:
+
+```liquid
+
+```
+
+## Collection Page Edits
+
+Next we'll add our target element(s) to the collection page. This is where the Searchspring elements will be injected into, typically two elements are added for a two-column layout: one for content, and one for facets.
+
+Targets are defined in your Snap configuration and will only be injected into if they exist on the page.
+
+- Online Store > Themes > [theme name] > ... > Edit code > Templates > `collection.liquid`.
+- `collection.liquid` is a standard Shopify template, but this may not be the file to edit depending on your theme. Look for includes which will tell you where to go, for example: `{% section 'collection-main' %}`. This says that there's additional code for the collection page located in the section file that has the name "collection-main".
+- Once the correct file is found, ensure that all of your search controller targets are added to the collection template. By using the `ss_enable` condition, we can retain Shopify's default functionality for when Searchspring is disabled via the theme settings.
+
+```liquid
+{% if settings.ss_enable %}
+
+{% else %}
+
+{% endif %}
+
+{% if settings.ss_enable %}
+
+{% else %}
+
+{% endif %}
+```
+
+## Autocomplete Form Submission
+
+Next we'll update the search form to submit to the search results collection page (ie. `/shop`) we created.
+
+- Online Store > Themes > [theme name] > ... > Edit code > Layout > `theme.liquid`.
+- Check for the search form. It may not be in this file, but check for its approximate location and then look in includes such as snippets or sections for its actual location. Other possible file names might be `form-search.liquid`, `header.liquid`, etc.
+- Create a copy of the form which you will edit and comment out the old form. This will allow us to retain Shopify's default functionality for when Searchspring is disabled via the theme settings.
+
+```liquid
+{% if settings.ss_enable %}
+
+{% else %}
+
+{% endif %}
+```
+
+In your form copy, update the following details:
+
+| Element | Attribute | Value |
+|---|---|---|
+| form | method | get |
+| form | action | `{{ routes.collections_url }}/{{ settings.ss_collection_handle }}` |
+| input[type="hidden"] | | remove any hidden inputs |
+
+
+## Integration Code
+
+Up until this point, we've added the Searchspring integration to the theme and collection page.
+
+Now we'll ensure our integration code captures the context variables.
+
+```js
+// src/index.js
+/* context from script tag */
+const context = getContext(['collection', 'tags', 'template', 'shopper', 'siteId']);
+```
+
+Below this, add code to support background filters on a `search` controller. As a best practice, we use `collection_handle` as this has unique values in comparison to collection name. Certain integrations also use tags to further filter products on collections (for example: `/collections/shirts/red` where `red` is a color tag), which is why we set an additional filter on `tags`. Additionally, this code snippet updates page details so you can use conditionals matching the current page in code.
+
+> [!NOTE]
+> Shopify has default collections to store vendors and types with urls of `/collections/vendors` and `/collections/types` respectively. To show a vendor or type, a query is applied to the url like `/collections/vendors?q=Awesome Brand`. To show results on these pages, in the code below we will take the page title from script context and apply it as a background filter with the `vendor` or `product_type` field.
+
+```js
+// src/index.js
+
+/* set up page details config */
+let isSearch = Boolean(window.location.href.includes('/shop'));
+let page = {
+ id: isSearch ? 'shop' : 'other',
+ title: isSearch ? 'Search Results' : 'Other Page',
+ type: isSearch ? 'search' : 'other',
+};
+
+/* background filters */
+let backgroundFilters = [];
+if (context.collection?.handle) {
+ // replace characters on collection name
+ const collectionName = context.collection.name.replace(/\&\#39\;/, "'");
+
+ // update page details when on collection
+ page = {
+ id: context.collection.handle,
+ title: collectionName,
+ type: 'collection',
+ };
+
+ // set background filter
+ if (context.collection.handle == 'vendors') {
+ backgroundFilters.push({
+ field: 'vendor',
+ value: collectionName,
+ type: 'value',
+ background: true,
+ });
+ } else if (context.collection.handle == 'types') {
+ backgroundFilters.push({
+ field: 'product_type',
+ value: collectionName,
+ type: 'value',
+ background: true,
+ });
+ } else {
+ backgroundFilters.push({
+ field: 'collection_handle',
+ value: context.collection.handle,
+ type: 'value',
+ background: true,
+ });
+ }
+
+ // handle collection tags (filters)
+ if (context.tags && Array.isArray(context.tags)) {
+ context.tags.forEach((tag) => {
+ backgroundFilters.push({
+ field: 'tags',
+ value: tag,
+ type: 'value',
+ background: true,
+ });
+ });
+ }
+}
+```
+
+To attach the `backgroundFilters` to a `search` controller, they need to be added into a `globals` config.
+
+```js
+// src/index.js
+import { sharedPlugin } from './plugins/sharedPlugin';
+const snap = new Snap({
+ client: {
+ globals: {
+ siteId: 'REPLACE_WITH_YOUR_SITE_ID',
+ },
+ },
+ controllers: {
+ search: [
+ {
+ config: {
+ id: 'search',
+ plugins: [[sharedPlugin, page]],
+ globals: {
+ filters: backgroundFilters,
+ },
+ },
+ targeters: [
+ {
+ selector: '#searchspring-content',
+ component: async () => {
+ return (await import('./components/Content/Content')).Content;
+ },
+ },
+ ],
+ },
+ ],
+ },
+});
+```
+
+## Update Product URLs
+
+For most Shopify sites, the product url should be updated to include the collection handle in the url path. Within a plugin that is attached to all controllers, add the below code. Adding this code to a "global" plugin makes the function reusable. For example: this function may be needed when clicking on a product swatch or size option to change the url with the collection handle.
+
+See example above to attach the plugin to the `search` controller.
+
+```js
+// src/plugins/sharedPlugin.js
+export const sharedPlugin = (controller, page) => {
+ // set initial custom settings for project
+ controller.store.custom = { ...controller.store.custom, page: page };
+ // update Shopify product url
+ controller.store.custom.updateUrl = (handle) => {
+ const { type, store } = controller;
+ const page = store.custom.page;
+
+ if (type == 'search') {
+ const hasRoute = typeof Shopify == 'object' && typeof Shopify.routes == 'object' && typeof Shopify.routes.root == 'string' ? true : false;
+ const routeShopify = hasRoute ? Shopify.routes.root : '/';
+ const routeCollection = page.type == 'collection' ? `collections/${page.id}/` : ``;
+ return `${routeShopify}${routeCollection}products/${handle}`;
+ }
+ };
+
+ controller.on('afterStore', async ({ controller }, next) => {
+ const page = controller.store.custom.page;
+ const { results } = controller.store;
+
+ if (page.type == 'collection' && results && results.length !== 0) {
+ results.forEach((result) => {
+ if (result.type != 'banner') {
+ result.mappings.core.url = controller.store.custom.updateUrl(result.attributes.handle);
+ }
+ });
+ }
+
+ await next();
+ });
+}
+```
+
+> [!NOTE]
+> Both code blocks above use the `page` config, so make sure that is defined.
+
+> [!WARNING]
+> Only use this function on product results, meaning do not use it if you have content tabs with blog articles. It should also not be run on Autocomplete, as it could result in invalid urls. For example: if you are on a collection page for "shoes" and search "shirts", this would format the url as "/collections/shoes" which could result in invalid urls for the results.
+
+
+## Additional Targets (Optional)
+
+In addition to having two targets for a two-column layout, you may want to inject content into other sections of the page such above the content and sidebar to display information such as the search query. Note the addition of `ss-shop` class before `searchspring-header` to ensure the content is only injected on the search page.
+
+```js
+// src/index.js
+targeters: [
+ {
+ selector: '#searchspring-content',
+ component: async () => {
+ return (await import('./content/content/Content')).Content;
+ },
+ hideTarget: true,
+ },
+ {
+ selector: '#searchspring-sidebar',
+ component: async () => {
+ return (await import('./sidebar/sidebar/Sidebar')).Sidebar;
+ },
+ hideTarget: true,
+ },
+ {
+ selector: '.ss-shop #searchspring-header',
+ component: async () => {
+ return (await import('./content/header/Header')).Header;
+ },
+ hideTarget: true,
+ },
+],
+```
+
diff --git a/docs/BUILD_DEPLOY_PERFORMANCE_OPTIMIZATION.md b/docs/BUILD_DEPLOY_PERFORMANCE_OPTIMIZATION.md
new file mode 100644
index 0000000000..ea4a140db4
--- /dev/null
+++ b/docs/BUILD_DEPLOY_PERFORMANCE_OPTIMIZATION.md
@@ -0,0 +1,166 @@
+# Performance Optimization
+
+## Script Loading Optimization
+
+### Script Placement
+
+Search and Category pages are critical to the initial display of content. Therefore it is recommended to install the Snap script tag in the `` so that content is fetched and displayed as soon as possible.
+
+
+### Async/Defer Script Attributes
+
+On Search and Category pages, it is not recommended to use `async` or `defer` attributes on the script tag as these will prevent the Snap script from executing immediately and will further delay the initial display of content.
+
+For other pages that only contain Recommendations, Autocomplete, or Finders (non-critical content), the `defer` attribute can be added conditionally to improve page load performance.
+
+### Resource Hints
+
+Use link tags with `preconnect` and `dns-prefetch` attributes to establish early connections to Snap CDN and Searchspring API domains:
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+
+## Optimizing Largest Contentful Paint (LCP)
+
+
+### Image Optimization
+
+For images that are part of the LCP element (above the fold), consider disabling lazy loading.
+
+The [Image](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-image--default) component has lazy loading enabled by default.
+
+Otherwise, if you are not using the `Image` component, ensure product images are optimized by setting the `loading` attribute to `lazy`:
+
+```jsx
+
+```
+
+### Prefetch Strategies
+
+Use `prefetch` sparingly and only for components that are likely to be viewed. When `prefetch` is enabled, the controller's search method is called immediately when the targeter is registered, before the target element is found in the DOM:
+
+```jsx
+const config = {
+ controllers: {
+ search: [{
+ config: {
+ id: 'search',
+ },
+ targeters: [{
+ selector: '#search-results',
+ prefetch: true // Triggers search immediately, before target is found
+ }]
+ }]
+ }
+};
+```
+
+Avoid prefetching when:
+- Component is below the fold
+- Component might not be needed (conditional rendering)
+- Page already has heavy resource usage
+
+## Optimizing Interaction to Next Paint (INP)
+
+### Optimize Event Middleware
+
+Snap's event middleware system allows you to hook into controller lifecycle events. When adding middleware, ensure you're not adding synchronous work that blocks the main thread:
+
+```jsx
+// Good: Use async middleware for non-blocking operations
+controller.on('beforeSearch', async ({ controller, request }, next) => {
+ // Perform async work that doesn't block interaction
+ await someAsyncOperation();
+ await next();
+});
+
+// Avoid: Adding heavy synchronous operations in middleware
+// This will delay INP response
+controller.on('beforeSearch', ({ controller, request }, next) => {
+ // Heavy synchronous computation - blocks INP
+ heavyComputation();
+ next();
+});
+```
+
+
+## Optimizing Cumulative Layout Shift (CLS)
+
+### Reserve Space for Components
+
+By adding a `min-height` to elements that are being targeted by Snap components, you can always reserve space for Snap components to prevent layout shifts. This is especially important for elements that are above the fold. By default, the `min-height` style will automatically be removed when the component is rendered.
+
+```html
+
+
+
+```
+
+### Use Skeleton Components
+
+We recommended using server-side rendered skeletons inside the target elements for the best LCP performance. If this is the case, also set the `renderAfterSearch` property to `true`.
+
+```html
+
+
+
+
+
+
+```
+
+```jsx
+import { Snap } from '@searchspring/snap-preact';
+
+const config = {
+ controllers: {
+ search: [{
+ config: {
+ id: 'search',
+ },
+ targeters: [{
+ selector: '#searchspring-content',
+ component: () => import('./Search'),
+ renderAfterSearch: true, // Render the skeleton after search is ready
+ }]
+ }]
+ }
+};
+```
+
+### Set Image Dimensions
+
+Ensure product images have defined dimensions to prevent layout shifts:
+
+```css
+.ss__image img {
+ width: 100%;
+ height: auto;
+ aspect-ratio: 1 / 1; /* Maintain aspect ratio */
+}
+```
+
+Or use the `style` prop on Image components:
+
+```jsx
+
+```
+
diff --git a/docs/GITHUB.md b/docs/GITHUB.md
deleted file mode 100644
index 1e65edd723..0000000000
--- a/docs/GITHUB.md
+++ /dev/null
@@ -1,83 +0,0 @@
-## Github Setup
-
-If you would like your final bundle and assets to reside on Searchspring's CDN (ie. `https://snapui.searchspring.io/siteid/bundle.js`), the repository must be in Searchspring's Github organization and an invitation can be requested for collaboration.
-
-Although it is not required to use Searchspring's organization nor Github for your version control, Snap utilizes Github Actions as part of our continuous integration and deployment.
-
-### Repository Configuration
-
-#### Default Branch
-
-The expected default branch name should be set to `production` instead of `main` or `master`
-
-#### Action Secrets
-
-For each Searchspring's Management Console account associated with the Snap repository, the following Action secret should be added as a repository secret. This page can be found at `https://github.com/[owner]/[repository]/settings/secrets/actions`
-
-- Secret Key Name: `WEBSITE_SECRET_KEY_[SITEID]`
-
-Where `[SITEID]` should be replaced with the siteId found in the Searchspring Management Console. For example: `WEBSITE_SECRET_KEY_ABC123`
-
-- Value: `secretKey` located adjacent to the siteId in the Searchspring Management Console
-
-
-### Github Action
-
-The [snap-action](https://github.com/searchspring/snap-action/) can be used by creating a new github workflow file (ie. `.github/workflows/deploy.yml`)
-
-The Snap Action requires additional parameters not shown in the example below. See [snap-action](https://github.com/searchspring/snap-action/) for additional documentation.
-
-```yml
-on: [push, pull_request]
-
-jobs:
- Publish:
- runs-on: ubuntu-latest
- name: Snap Action
- steps:
- - name: Checkout action
- uses: actions/checkout@v2
- with:
- repository: searchspring/snap-action
- - name: Run @searchspring/snap-action
- uses: ./
-```
-
-### Project configuration
-
-#### package.json
-
-The package.json file must contain all siteIds associated with this project.
-
-Single siteId example:
-
-```json
-{
- ...
- "searchspring": {
- "siteId": {
- "abc123": {
- "name": "site1.com"
- }
- },
- }
-}
-```
-
-Multi siteId example:
-
-```json
-{
- ...
- "searchspring": {
- "siteId": {
- "abc123": {
- "name": "site1.com"
- },
- "def456": {
- "name": "site1.com.au"
- }
- },
- }
-}
-```
\ No newline at end of file
diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md
deleted file mode 100644
index 7f0a10b6d7..0000000000
--- a/docs/INTEGRATION.md
+++ /dev/null
@@ -1,33 +0,0 @@
-## Integration
-
-When development has concluded the bundle is ready to be placed on a development or production site.
-
-```html
-
-```
-
-The bundle should be included in the `` tag, ideally near the top of the node, and should not have a 'defer' or 'async' attribute. This location is important in order to start fetching results and as soon as possible. This placement prior to any body elements also serves to allow for the hiding of targeted elements that contain content - this preventing a flash when the contents change upon injection.
-
-Context variables should be placed inside the script tag, see the documentation for [context variables](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_CONTEXT.md) for more details.
-
-```html
-
-
-
-
-
-
- Snap Integration Example
-
-
-
-
-
-
-
-
-```
\ No newline at end of file
diff --git a/docs/INTEGRATION_DEBUGGING.md b/docs/INTEGRATION_DEBUGGING.md
deleted file mode 100644
index 0d2311079f..0000000000
--- a/docs/INTEGRATION_DEBUGGING.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## Debugging
-
-## Branch Overrides
-
-This functionality is only currently possible with Searchspring managed Snap repositories (https://github.com/searchspring-implementations).
-
-While browsing a page that contains a Snap integration, appending the `?searchspring-preview=[branchname]` query parameter to the URL will stop the execution of the existing script, and load the build from the `[branchname]` branch `https://snapui.searchspring.io/[siteid]/[branchname]/bundle.js`
-
-You will see an interface overlay on the bottom right of the viewport indicating if successful and details of the build.
-
-
-
-This will also be persisted across page navigation. To stop previewing a branch build, you must click the `Stop Preview` button in the interface or clear the `ssBranch` cookie. The interface can also be minimized.
-
-
-## Development Mode
-
-While browsing a page that contains a Snap integration, appending the `?dev` query parameter to the URL will set the controller's `environment` property to `development`.
-
-This is commonly used to enable visibility of development logs in the console.
-
-See [AbstractController](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Abstract) and [@searchspring/snap-logger](https://github.com/searchspring/snap/tree/main/packages/snap-logger)
-
diff --git a/docs/INTEGRATION_VARIANTS.md b/docs/INTEGRATION_VARIANTS.md
index 285bb99b49..4be5ac144a 100644
--- a/docs/INTEGRATION_VARIANTS.md
+++ b/docs/INTEGRATION_VARIANTS.md
@@ -9,48 +9,7 @@ Product variants allow you to represent different versions of the same base prod
Snap's variants functionality helps manage these product variations by providing tools to configure, display and interact with variant data in your components.
-Configure variants by setting the variant field in either:
-- Controller config: `controllers[controller].config.settings.variants.field`
-- Recommendation config: `instantiators.recommendation.config.settings.variants.field`
-
-Example -
-
-```typescript
-const config = {
- instantiators: {
- recommendation: {
- components: {
- Bundle: async () => (await import('./components/Recommendations/Bundle/Bundle')).Bundle,
- },
- config: {
- settings: {
- variants: {
- field: 'ss_variants',
- },
- },
- },
- },
- },
- controllers: {
- search: [
- {
- config: {
- id: 'search',
- settings: {
- variants: {
- field: 'ss_variants',
- },
- },
- },
- },
- ],
- },
-};
-
-const snap = new Snap(config);
-```
-
-Once configured, each result that has variants in `controller.store.results` should include a `variants` object with:
+The Athos API will automatically return variants data when applicable. When this happens, each result that has variants in `controller.store.results` will include a `variants` object with:
**Properties:**
- `active` - Currently selected variant data
@@ -175,7 +134,6 @@ const config = {
config: {
settings: {
variants: {
- field: 'ss_variants',
realtime: {
enabled: true,
},
@@ -234,7 +192,6 @@ You can filter which results update in realtime by adding filters to your config
Example -
```typescript
variants: {
- field: 'ss_variants',
realtime: {
enabled: true,
filters: ['first']
diff --git a/docs/PREACT.md b/docs/PREACT.md
deleted file mode 100644
index 46aa55f31f..0000000000
--- a/docs/PREACT.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## Snap Preact
-
-The [@searchspring/snap-preact](#/package-preact) package is an abstraction layer for Preact that provides a config based interface for creating a Searchspring integration quickly. Underneath the hood it utilizes all of the core Snap packages. If you wish to create a Snap integration using core packages individually (hard mode), see the Advanced section.
-
-If you are not using Snapfu to start with a template, you will need to start by adding Snap to your project.
-
-```bash
-npm install --save @searchspring/snap-preact
-```
-
-```typescript
-import { Snap } from '@searchspring/snap-preact';
-```
\ No newline at end of file
diff --git a/docs/PREACT_CONFIG.md b/docs/PREACT_CONFIG.md
deleted file mode 100644
index 35bfcf04f0..0000000000
--- a/docs/PREACT_CONFIG.md
+++ /dev/null
@@ -1,149 +0,0 @@
-## Configuration
-
-Let's define our config. The config that is provided to Snap will create and return controllers that are specified in the config. In this example, we will be creating a Search and Autocomplete controller.
-
-```typescript
-const config = {
- features: {
- integratedSpellCorrection: {
- enabled: true,
- },
- },
- url: {
- parameters: {
- core: {
- query: { name: 'query' }
- }
- }
- },
- client: {
- globals: {
- siteId: 'xxxxxx',
- },
- },
- controllers: {
- search: [
- {
- config: {
- id: 'search',
- },
- targeters: [
- {
- selector: '#searchspring-content',
- component: () => Content,
- skeleton: () => ContentSkel,
- hideTarget: true,
- },
- {
- selector: '#searchspring-sidebar',
- component: () => Sidebar,
- skeleton: () => SidebarSkel,
- hideTarget: true,
- },
- ],
- },
- ],
- autocomplete: [
- {
- config: {
- id: 'autocomplete',
- selector: 'input.searchspring-ac',
- settings: {
- trending: {
- limit: 5,
- },
- },
- },
- targeters: [
- {
- selector: 'input.searchspring-ac',
- component: () => Autocomplete,
- hideTarget: true,
- },
- ],
- },
- ],
- },
-};
-```
-
-Let's go over a few things.
-
-`config.url` is optional and contains a [`UrlTranslator` config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) object that is passed to the core [@searchspring/snap-url-manager](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager) package used by all controllers. This parameter configuration will be applied to all controllers created via Snap, but can be specified per controller for specific customization.
-
-`config.client` is required and contains a config object that is passed to the core [@searchspring/snap-client](https://github.com/searchspring/snap/tree/main/packages/snap-client) package. This service handles the network requests to our APIs to retrieve data to be displayed.
-
-`config.client.globals` specifies base query parameters to the API; these are parameters that will ALWAYS be included on every request. At the bare minimum, `siteId` is required and can be obtained in the [Searchspring Management Console](https://manage.searchspring.net/)
-
-`config.controllers` specifies all of the controllers that we wish to create. In this this example we are creating a Search and Autocomplete controller. In addition, Finder and Recommendation services can also be specified.
-
-### Search
-
-Lets look at the Search controller that we are creating.
-
-The `config` object contains all controller configurations. The most notable property here is the required `id` with a given value of `'search'`. This will be the name of the search controller that we can then interface with the return of the `new Snap()` instance via the `getController` method. In snap-preact controllers are created only as needed (typically when a targeter has found a target), their creation is an asynchronous process. The `getController` method will return a promise that will resolve to the controller object requested immediately after its creation.
-
-For example:
-
-```typescript
-const snap = new Snap(config);
-snap.getController('search').then((search) => {
- // do things with controller
-});
-```
-
-If multiple controllers are needed at the same time, usage of the `getControllers` method is necessary. The `getControllers` method returns a promise that resolves to an array of controllers in the order requested by the parameters. The promise only resolves when ALL of the controllers have been created - if a controller is specified that is never created the promise will never resolve. For this reason this method should only be used when all controllers are needed simultaneously.
-
-```typescript
-const snap = new Snap(config);
-snap.getControllers('search', 'autocomplete').then(([search, autocomplete]) => {
- // do things with controllers
-});
-```
-
-We also have a `targeters` array of DomTargeter `targeter` configuration objects. Each object defines an entry point on the page where a component will be rendered.
-
-`targeter.selector` specifies the DOM selector to target
-
-`targeter.component` specifies a function that returns a reference to the component to render at the target selector.
-
-`targeter.hideTarget` boolean that specifies if the target node should be hidden before the component is mounted and rendered. It is recommended to enable this to prevent flashy behaviour.
-
-`targeter.autoRetarget` (optional) boolean that specificies if the targeter should continuously query for the selector in the DOM until it finds it and triggers a retarget. This is useful for dynamically generated selectors that might not exist at dom ready.
-
-`targeter.skeleton` (optional) meant to be used as a "loading" component. This specifies a function that returns a reference to the component to render immediately at the target selector to show briefly while the data is returning and the real component is rendering. You can use any component you want for this, although `snap-preact-components` provides a `skeleton` component for you to use if preferred.
-
-`targeter.props` (optional) convenient way of passing additional props to the component, by default we pass `controller`
-
-`targeter.onTarget` (optional) callback that fires after a target is found
-
-`targeter.name` (optional) name to give the targeter for later reference using `controller.targeters`
-
-In our example, we are rendering a `` component into `
` and the `` component into `
`
-
-
-
-### Autocomplete
-
-We're also creating an Autocomplete controller in a similar function.
-
-One notable thing to mention as you may see a duplicate `selector` property in both the `config` and `targeter`.
-
-The `config.selector` specifies the `` element(s) to attach events to that respond to Autocomplete actions. This supports a selector that targets many elements.
-
-The `targeter.selector` specifies the DOM node where the `targeter.component` will be rendered into.
-
-However in our example, since they are both the same value, the Autocomplete component will rendered as a child DOM node below the `` element that is currently focused.
-
-
-### Feature Flags
-
-`config.features` is optional and defines features to enable.
-
-#### Integrated Spell Correction
-
-Integrated spell correction is disabled by default. When disabled and a query is typed into autocomplete, a request is made to the suggest API to retrieve a list of terms. The highest scoring term is then used to query the search API for results.
-
-Enabling integrated spell correction `config.features.integratedSpellCorrection.enabled = true` will still retrieve terms from the suggest API to display, however the query that was entered will be used as the term sent to the search API. Spell correction will occur within the search API. The correction and original query is returned in the response and available to be render. Upon submitting the autocomplete form, a `fallbackQuery` parameter is also submitted. This contains a value of the highest scoring suggested term and will be searched for if the initial query yields 0 results.
-
-Note: Enabling integrated spell correction modifies [AutocompleteController](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Autocomplete)'s config by setting `config.settings.integratedSpellCorrection = true`
diff --git a/docs/PREACT_CONTROLLER_PROPS.md b/docs/PREACT_CONTROLLER_PROPS.md
deleted file mode 100644
index 1a1f75eab2..0000000000
--- a/docs/PREACT_CONTROLLER_PROPS.md
+++ /dev/null
@@ -1,93 +0,0 @@
-## Controller Props
-
-For each targeted element, the corresponding controller that created it will be passed along to the component via the `controller` prop.
-
-Let's go over our `Content` component. This is considered a root level component since it is being rendered onto the page using a targeter.
-
-We'll want to create a `ControllerProvider` such that any subcomponents can have a reference to the controller via its props (as long as it is using the cooresponding `withController` consumer). The [@searchspring/snap-preact-components](https://github.com/searchspring/snap/tree/main/packages/snap-preact-components) package contains a `ControllerProvider` that we can utilize.
-
-
-```jsx
-import { h, Fragment, Component } from 'preact';
-import { observer } from 'mobx-react-lite';
-import { ControllerProvider } from '@searchspring/snap-preact-components';
-import { Results, NoResults } from './Results';
-
-@observer
-export class Content extends Component {
- render() {
- const controller = this.props.controller;
-
- return (
- controller.store.loaded && (
-
- {
- controller.store.pagination.totalResults > 0 ? () : ()
- }
-
- )
- );
- }
-}
-```
-
-Then from any subcomponent such as the `Result` component in this example, we'll need to add the `@withController` decorator to access our controller via props. The `@withController` decorator should be placed before any other decorators.
-
-```jsx
-import { h, Fragment, Component } from 'preact';
-import { observer } from 'mobx-react-lite';
-import { withController, InlineBanner, Result } from '@searchspring/snap-preact-components';
-
-@withController
-@observer
-export class Results extends Component {
- render() {
- const controller = this.props.controller;
- const { results } = controller.store;
-
- return (
-
- {results.map((result) => (
-
- {{
- banner: ,
- }[result.type] || }
-
- ))}
-
- );
- }
-}
-```
-
-
-## Reactivity
-
-Each controller has a `store` property. This is a MobX store created from the core [@searchspring/snap-store-mobx](https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx) package and we will be rendering its data in our components.
-
-The MobX store contains many observable properties that are reactive to changes such as user interaction (ie. collapsing a facet) or fetching data (ie. pagination). In order to react to these store changes the `@observer` decorator must be added to our components.
-
-If you are creating functional components you would use this as a function wrapping your component.
-
-```jsx
-import { h, Fragment } from 'preact';
-import { observer } from 'mobx-react-lite';
-import { withController, InlineBanner, Result } from '@searchspring/snap-preact-components';
-
-export const Results = observer(withController((props) => {
- const controller = props.controller;
- const { results } = controller.store;
-
- return (
-
- {results.map((result) => (
-
- {{
- banner: ,
- }[result.type] || }
-
- ))}
-
- )
-}));
-```
diff --git a/docs/PREACT_DISPLAYING_DATA.md b/docs/PREACT_DISPLAYING_DATA.md
deleted file mode 100644
index a0b06d83d8..0000000000
--- a/docs/PREACT_DISPLAYING_DATA.md
+++ /dev/null
@@ -1,479 +0,0 @@
-## Displaying Data
-
-At this point you are ready to start building components that render data from the controller's store. Here are a few common store properties and suggested usage in components. If you have used Snapfu to start with a template, these component examples will already be included.
-
-## All Stores
-
-All of the following properties are available on all stores (Search, Autocomplete, Finder, & Recommendations)
-
-### controller.store.loaded
-
-The `loaded` property will be true when the store has been loaded with data and is available to be consumed. This property is recommended to conditionally render a component.
-
-### controller.store.loading
-
-The `loading` property will be true is a network request is in progress. This property is recommended to conditionally render a loading status (ie. spinning icon or loading bar)
-
-### controller.store.custom
-
-See [`custom` property](https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Abstract)
-
-## Search Store
-
-The following properties are specific to a Search Store via a Search Controller.
-
-
-### SearchController.store.merchandising
-
-The `merchandising` property contains merchandising redirects and banner content. It is recommended to utlizing the `` component from `@searchspring/snap-preact-components` to display the various merchandising banners.
-
-The available banner types include: `header`, `banner`, `footer`, `left`, `inline`
-
-For inline banners, the `` component should be used instead. An example of this usage can be found in the 'store.results' section below.
-
-```jsx
-import { Banner } from '@searchspring/snap-preact-components';
-
-@observer
-export class Content extends Component {
- render() {
- const controller = this.props.controller;
- const { store } = controller;
- const { pagination, merchandising } = store;
-
- return (
- store.loaded && (
-
-
- )
- );
- }
-}
-```
-
-### SearchController.store.pagination
-
-The `pagination` property is not only used for information about the current query, but also contains everything needed for handling pagination of a query that yields multiple pages. Invoking the `getPages` method will retrieve the specified number of page objects. For more about the pagination store, checkout the [Search Controller docs](#/package-controller-search).
-
-```jsx
-@withController
-@observer
-export class Pagination extends Component {
- render() {
- const controller = this.props.controller;
- const {
- store: { pagination },
- } = controller;
- const pages = pagination.getPages(5);
-
- return (
-
- );
- }
-}
-```
-
-### SearchController.store.sorting
-
-The `sorting` property contains sorting options applicable to the current query. Typically used to render a `