Git Product home page Git Product logo

storefront-product-listing-page's Introduction

storefront-product-listing-page

Product Listing Page for Adobe Commerce Storefronts using Live Search

The product listing page provides coverage for both search and browse (PLP) results and includes the faceting, sorting, and product card areas on the page. This is the recommended default storefront PLP provided by Live Search. It provides a search experience that is client side rendered and hosted with a decoupled architecture.

The PLP calls the catalog service which extends the Live Search productSearch query to return product view data. This allows the PLP to render additional product attributes like swatches with a single call.

Learn more:

Repo containing the Live Search PLP

This repo is provided as a reference implementation only. While you’re expected to create your own customizations based on these assets, those customizations can’t be supported by Adobe.

Best practices include:

  • forking this repo
  • periodically rebasing with develop

Setup

Requirements

Node and NPM setup: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm

  • Node version: v16 or greater
  • NPM version: v8 or greater

NVM is a nifty tool to help toggle between enironment versions

Install dependencies

Within the root of the project (~/storefront-product-listing-page):

npm install

Storybook (mdx files)

Install storybook: https://storybook.js.org/docs/react/get-started/install

npm run storybook

Local Development

build tailwind styles and run locally

npm run tailwind:build
npm run dev

note: for styles to update you must run npm run tailwind:build

And open localhost:8080/v1/index.html in your favorite browser.

Unit Testing

npm run test

Build

npm run tailwind:build //builds tailwind styles
npm run build

search.js will be built to ./dist/

Example setup:

import the script:

<script type="text/javascript" src="search.js"></script>

Setup the Store Details:

Most of these will be passed with the extension if you have your storefront setup. The SANDBOX_KEY (api key for the sandbox env) is the only key that will need to be set within webpack.

Store Variables needed:

      ENVIRONMENT_ID
      WEBSITE_CODE
      STORE_CODE
      STORE_VIEW_CODE
      CUSTOMER_GROUP_CODE
      API_KEY
      SANDBOX_KEY // input this key into webpack.dev.js & webpack.prod.js

insert into store details config

const storeDetails = {
      environmentId: 'ENVIRONMENT_ID',
      websiteCode: 'WEBSITE_CODE',
      storeCode: 'STORE_CODE',
      storeViewCode: 'STORE_VIEW_CODE',
      config: {
        minQueryLength: '2',
        pageSize: 8,
        perPageConfig: {
          pageSizeOptions: '12,24,36',
          defaultPageSizeOption: '24',
        },
        currencySymbol: '$',
        currencyRate: '1',
        displaySearchBox: true,
        displayOutOfStock: true,
        allowAllProducts: false,
        currentCategoryUrlPath?: string;
        categoryName: '', // name of category to display
        displaySearchBox: false, // display search box
        displayOutOfStock: '', // "1" will return from php escapeJs and boolean is returned if called from data-service-graphql
        displayMode: '', // "" for search || "PAGE" for category search
        locale: '', //add locale for translations
        priceSlider: false, //enable slider for price - EXPERIMENTAL, default is false
        imageCarousel: false, //enable multiple image carousel - EXPERIMENTAL, default is false
        listview: false; //add listview as an option - EXPERIMENTAL, default is false
        optimizeImages: true, // optimize images with Fastly
        imageBaseWidth: 200,
        resolveCartId?: resolveCartId // Luma specific addToCart method. Enabled with the extension
        refreshCart?: refreshCart // Luma specific addToCart method. Enabled with the extension
        addToCart?: (sku, options, quantity)=>{} // custom add to cart callback function. Called on addToCart action
      },
      context: {
        customerGroup: 'CUSTOMER_GROUP_CODE',
      },
      apiKey: 'API_KEY',
    };

Append LiveSearchPLP to the storefront window:

const root = document.querySelector('div.search-plp-root');

setTimeout(async () => {
      while (typeof window.LiveSearchPLP !== 'function') {
        console.log('waiting for window.LiveSearchPLP to be available');
        await new Promise((resolve) => setTimeout(resolve, 500));
      }
      window.LiveSearchPLP({ storeDetails, root });
}, 1000);

You can see the example in dev-template.html

Theming and Styling

We use Tailwind to style/theme our components. Here's a good read up if you are keen: Tailwind Docs

In addition to this themeing, we have CSS classes where storefronts can target and customize specific components of the widget. CSS classes will be used to target specific components or elements of components for a more granular approach. In short, CSS variables can create a general theme and CSS classes can target specific elements in our widget.

With Tailwind we do not write custom CSS. Instead, we use its utility classes that are generated at buildtime using a config we provide and scanning our markup (this way we don't have extra CSS we don't need). This is our config file.

So how do we use CSS variables to style our components? Great question 😄

Let's say as if I want to style an element with the theme's primary color. Normally, in CSS we would have done the following:

<style>
 .primaryColor {
    color: var(--primary-color)
 }
</style>

<div class="primaryColor">
  Yippee I am a primary color!
</div>

Using Tailwind the following produces the exact same result:

<div class="text-primary">
  Yippee I am a primary color!
</div>

Looking at the config file you will notice that the CSS variable is --color-primary is mapped to the Tailwind CSS theme color key primary. this means anywhere in Tailwind you would use a Color key in a class you can now use the word primary.

You can add your own variables to tokens.css. Furthermore, you can define your own tailwind classes using these variables in the config file.

Have tailwind only apply to the nested widget

Now default behavior:

Follow the tailwind nesting documentation. Your postcss.config will look like this:

module.exports = {
  plugins: [
    require('postcss-import'),
    require('tailwindcss/nesting'),
    require('autoprefixer'),
    require('tailwindcss'),
    require('cssnano'),
  ],
};

From there you should be able to nest the tailwind classes:

@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

within .ds-widgets in token.css

.ds-widgets {
  @import 'tailwindcss/base';
  @import 'tailwindcss/components';
  @import 'tailwindcss/utilities';

  ...
}

Some helpful tools when developing with tailwind:

Developing with React

To extend and customize this repo, you must first be familiar with react:

Example Implemention - adding a button to ProductItem:

export const ProductItem: FunctionComponent<ProductProps> = ({
  item,
  currencySymbol,
  currencyRate,
  setRoute,
  refineProduct,
}: ProductProps) => {
  ...

  const handleClickButton = () => {
    console.log('clicked the button!');
  }

  return (
    <div className="ds-sdk-product-item group relative flex flex-col max-w-sm justify-between h-full">
        ...
          <div className="flex flex-col">
            <div className="ds-sdk-product-item__product-name mt-md text-sm text-primary">
              {htmlStringDecode(productView.name)}
            </div>
            <ProductPrice
              item={refinedProduct ?? item}
              isBundle={isBundle}
              isGrouped={isGrouped}
              isGiftCard={isGiftCard}
              isConfigurable={isConfigurable}
              isComplexProductView={isComplexProductView}
              discount={discount}
              currencySymbol={currencySymbol}
              currencyRate={currencyRate}
            />
            <button onClick={handleClickButton}>I am a button!</button> // added a button
          </div>
        </div>
      </a>

    </div>
  );
};

For more robust examples, please see our Examples folder

Hosting with CDN

Follow the below instructions on how to host with CDN and update your Commerce store to include your new product listing page.

  1. In the root of this repository, install dependencies: npm install
  2. Create a production build of the application: npm run build
  3. After the previous step succeeds, it will output a search.js file in the dist/ directory. This is the production-ready product listing page.
  4. Follow the instructions from your favorite frontend hosting with CDN (we use AWS S3 + Cloudfront, but there are many options to choose from) to upload the search.js file.
  5. The hosting platform with CDN will provide you a url where the search.js file will be publicly hosted for your consumption.
  6. Take the url where the search.js file is hosted and validate in your browser that it is working correctly by loading the url in your browser and seeing the search.js file returned to you.
  7. Now the Live Search extension needs to be updated to include your new url. Find the magento-live-search extension code in your adobe commerce code base. Locate the directory LiveSearchProductListing.
  8. We will be updating two files under magento-live-search > LiveSearchProductListing: etc/config.xml & etc/csp_whitelist.xml
  9. Changes for etc/config.xml: change the live search url in <frontend_url>to match the one you created with your hosting/CDN solution url above. See below.
  10. image
  11. Changes for etc/csp_whitelist.xml: find all references to the live search SaaS product list page (plp-widgets-ui.magento.ds.com) and replace with your hosting/CDN solution url. See below.
  12. image
  13. Those are all the required changes. Now redeploy your adobe commerce store code and see your new custom product listing page in your storefront.

The purpose of this project is to provide a dev-ready starting point for developers to implement the product listing page. The project, repo, and any accompanying assets included herein (“Assets”) are provided “as-is” for your use solely at your sole discretion, risk, and responsibility. By using these Assets, you agree Adobe will in no event be responsible for any use of the Assets, including but not limited to any customizations made thereto, by you or any third party. Adobe will not provide any support of any kind for any customizations made to the Assets by anyone.

storefront-product-listing-page's People

Contributors

aniham avatar herzog31 avatar kathleentynan avatar sharkysharks avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

storefront-product-listing-page's Issues

Support urlKey in route

Expected Behaviour

Pass a product's urlKey to the route function which returns the product page URL for a product.

const storeDetails = {
  route: ({ sku, urlKey }) => `/products/${urlKey}/${sku}`,
};

Actual Behaviour

Currently only the product sku is passed.

Cannot install

Expected Behaviour

npm install runs successfully

Actual Behaviour

npm install fails

npm i
npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY
npm ERR! errno UNABLE_TO_GET_ISSUER_CERT_LOCALLY
npm ERR! request to https://artifactory.corp.adobe.com/artifactory/api/npm/npm-adobe-release/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz failed, reason: unable to get local issuer certificate

npm config set strict-ssl false

npm i
npm ERR! code E401
npm ERR! 401 Unauthorized - GET https://adobe.trust-adobe.console.banyanops.com/v2/auth?client_id=ePcIQuesF41DxqDK3gTuUw&nonce=LVfLrA2f25xoqKD7x1kKq&redirect_uri=https%3A%2F%2Fartifactory.corp.adobe.com%2Fbnn_trust_cb&response_type=code&scope=openid+profile+email&state=tUQhQR41TgWPsVMwvquIhA

Reproduce Scenario (including but not limited to)

clone this repository
npm i

Steps to Reproduce

clone this repository
npm i

Platform and Version

Sample Code that illustrates the problem

Logs taken while reproducing problem

Unable to add id,uid,categories in the Graphql Request

Expected Behaviour

Able to add id, uid, categories in the api/fragment.ts Graphql Query

Actual Behaviour

unable to add id, uid, categories in the api/fragment.ts Graphql Query

Reproduce Scenario (including but not limited to)

Add the id, uid, categories in the api/fragment.ts Graphql Query

Steps to Reproduce

categories in the api/fragment.ts Graphql Query

Platform and Version

Magento 2.4.5-P7 and Live search

Sample Code that illustrates the problem

Logs taken while reproducing problem

Screenshot from 2024-06-20 15-49-19

Screenshot from 2024-06-20 15-35-03

List view config option should use camel case

Expected Behaviour

In order to maintain a consisten API the listview store config property should be listView.

Actual Behaviour

All other store config properties use camel case expect for listview.

Storybook command broken

We forked the project and the Storybook command just didn't work.

  1. The command itself doesn't run storybook, I had to add the & instead of semicolon and it ran storybook with no stories.
  2. I moved the content inside the storybook.config.js into main.ts in storybook folder instead and it ran the stories.
  3. Received a src/context not found error which I added an alias to storybook config to handle.
  4. Now receiving a Failed to execute 'createElement' on 'Document': The tag name provided ('static/media/src/icons/cart.svg') is not a valid name.

Error with search.js (v1.1.0): 'Client ID is invalid'

Actual Behaviour

I followed the instructions provided in the README to build search.js version 1.1.0 for a Magento website. However, when I integrated the file into the website, I encountered an error stating 'Client ID is invalid.' from the request https://catalog-service-sandbox.adobe.io/graphql

Interestingly, I noticed that there is no issue when using the version v1.0.4, which comes from the URL https://plp-widgets-ui.magento-ds.com/v1/search.js.

Could there be issues in v1.1.0 that are not present in v1.0.4?

Expected Behaviour

Search.js should function without errors and provide the expected search functionality on the website.

Steps to Reproduce

  1. Build search.js by running npm run build.
  2. Integrate search.js into a website, which is Magento in my case.
  3. The search result page is blank and the endpoint https://catalog-service-sandbox.adobe.io/graphql returns an error message 'Client ID is invalid'.
{
    "error": {
        "code": "Forbidden",
        "message": "Client ID is invalid",
        "details": {
            "error_code": "403003"
        }
    }
}

Platform and Version

Adobe Magento Commerce ver. 2.4.7

Installation warning when running npm run dev command

Expected Behaviour

When running npm run dev command there should not be any warnings and the default browser page should load.

Actual Behaviour

When running npm run dev command getting warnings as below. Please note I am running on a windows machine.

warn - No utility classes were detected in your source files. If this is unexpected, double-check the content option in your Tailwind CSS configuration.
warn - https://tailwindcss.com/docs/content-configuration

Steps to Reproduce

  1. Install node (v20.9.0)
  2. install npm (10.1.0)
  3. npm install
  4. npm run storybook
  5. npm run dev

npmrundev

Platform
Edition:- Windows 10 Enterprise
Version:- 22H2

Support Fastly Image Optimizer

Expected Behaviour

  • Leverage Fastly Image Optimizer that is included in Adobe Commerce to dynamically resize images and deliver them in optimized formats.
  • Improve performance by loading images in dimensions as needed by the current rendering.
  • Make the feature configurable.

Actual Behaviour

  • Images are rendered as they are returned by the Live Search API. No image processing is taking place.
  • This impacts performance, since images are loaded and rendered that are too large.

Improve developer experience by enabling sourcemaps

Expected Behaviour

During the development of a storefront being able to easily debug in a browser is crucial. Currently the only source available for debugging is the result after going through the webpack bundling.

Actual Behaviour

Include sourcemaps with the webpack developer configuration.

Discussion section

Request

Can there be a discussion section in the repository? So that developers forking this can ask questions without submitting issues or having to contact Adobe directly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.