Extension Review Guidelines - Frontend

Must haves

Testing

Only submit functional code. Remove all commented out code and do not submit code that is not executed.

To provide the best possible feedback on your integration, Shopgate focuses on the final functionality you want to deliver. Shopgate does not provide alpha or beta testing services. Remember to test your extension on both themes (GMD and iOS11), including edge cases (e.g. slow connection).

Imports

Do not import something directly from the Shopgate Themes. This approach may work during development, but it will break in production. Instead, use the components from our Engage package or via theme API.

Please contact Shopgate if you want to use any component from the theme that is not yet available via the theme API.

❌ Wrong

import ProductSlider from '../../../themes/theme-gmd/components/ProductSlider';
import ProductSlider from 'Components/ProductSlider';
✅ Right

import { useTheme } from '@shopgate/engage/core';

function MyComponent() {
  const { ProductSlider } = useTheme();

  return (
    <div>
      <ProductSlider />
    </siv>
  );
}

Use the Shopgate translation system

Shopgate provides a built-in translation system with some helpers. You should not hardcode any strings in your code. Put all text into a JSON file. For more information, see the adding custom translations tutorial.

❌ Wrong

MyComponent = () => (
  <h1>My custom headline</h1>
)

✅ Right

import { I18n } from '@shopgate/engage/components';

MyComponent = () => (
  <h1>
    <I18n.Text string="myExt.headline" />
  </h1>
)

Scope your translation string with a unique name

To avoid overriding any existing string from the Shopgate themes or other extensions, scope your strings in the local files. Shopgate recommends that you use the ID of your extension as the scope here.

If you want to explicitly override an existing string, you must not scope it.

❌ Wrong

// locale/en-US.json
{
  "headline": "A nice headline"
}
✅ Right

// locale/en-US.json
{
    "@myOrga/myExt": {
      "headline": "A nice headline"
    }
}

Declare dependencies to PWA

Add a peer dependency to your frontend/package.json to specify the minimum required PWA version for your extension.

✅ Right

"peerDependencies": {
 "@shopgate/pwa-common": "^6.5.1",
 "@shopgate/pwa-common-commerce": "^6.5.1",
 "@shopgate/pwa-ui-shared/Price": "^6.5.1"
}

Colors and other variables

Use the Shopgate provided color and size variables rather than hard coding colors and sizes, where possible. This approach automatically configures your components using merchant colors and ensures that your components match the other App components.

❌ Wrong

const myPrimaryElement = css({
 	background: 'red',
});

✅ Right

import { themeConfig } from '@shopgate/pwa-common/helpers/config';

const myPrimaryElement = css({
 	background: themeConfig.colors.primary,
});
}

CSS selectors

Do not use Shopgate classNames or internal DOM structure to build CSS selectors.

Shopgate classNames are auto-generated, generic names that change with new PWA versions. The same rules apply to Shopgate component markup. Instead, add your own classNames and use them for styling. See our styling tutorial

❌ Wrong

<MyComponent>
  <LibComponent />
</MyComponent>

.css-1p3dfz6 {
	font-size: 20px;
}
.footer > div > ul {
  display: none
}
✅ Right

<MyComponent>
  <LibComponent className=”my-class/>
</MyComponent>

.my-class {
  font-size: 20px;
}

Codestyle

Your code should be easy to read and follow some kind of consistent codestyle across your files. Shopgate recommends that you use our codestyle (which is based on airbnb) https://www.npmjs.com/package/@shopgate/eslint-config.

❌ Wrong

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import style from './style';
class Button extends Component {
   get buttonProps() {
   const {
     children, testId, className, disabled, onClick, ...props
   } = this.props;    const buttonProps = {
     className: `${className} ${style}`,
     disabled,
     onClick: disabled ? null : onClick,
     ...props,
   };
   return buttonProps;
 }
 render() {
   return (<button data-test-id={this.props.testId} {...this.buttonProps}>{this.props.children}
     </button>
   );}}export default Button;
✅ Right

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import style from './style';

/**
* The button component.
*/
class Button extends Component {
 static propTypes = {
   children: PropTypes.node.isRequired,
   className: PropTypes.string,
   disabled: PropTypes.bool,
   onClick: PropTypes.func,
   testId: PropTypes.string,
 };

 static defaultProps = {
   className: '',
   disabled: false,
   onClick: null,
   testId: 'Button',
 };

 /**
  * Getter for the calculated button props.
  * @returns {Object}
  */
 get buttonProps() {
   const {
     children, testId, className, disabled, onClick, ...props
   } = this.props;

   const buttonProps = {
     className: `${className} ${style}`,
     disabled,
     onClick: disabled ? null : onClick,
     ...props,
   };

   return buttonProps;
 }

 /**
  * Renders the component.
  * @returns {JSX}
  */
 render() {
   return (
     <button data-test-id={this.props.testId} {...this.buttonProps}>
       {this.props.children}
     </button>
   );
 }
}

export default Button;

Major

Error Handling

Use proper error handling. For example:

  • Never show hard errors to the user (e.g. some HTTPS status codes). Always provide a proper error message for these situations.

    ❌ Wrong
    
    "Internal Server error: can not read property check of undefined. someFile.js L12:13"
    
    ✅ Right
    
    "Sorry, something went wrong. Please try again"
    
  • Allow retries, if applicable.

  • Use proper error codes. You can find predefined error codes here: https://developer.shopgate.com/extension-review-guidelines/general-error-codes.

  • Ensure your UI still works and provides the user with feedback in case of an error.

Caching

If you send any requests to the backend, cache the response in the frontend in some way. This approach increases the performance of your app and reduces the load on the backend. See Fetching and processing data for more information.

Placeholders

If your UI displays something that needs to be fetched before, be sure to indicate this to the user and reserve some space for the content. Otherwise, the UI could jump as soon as the response comes in.

In addition to some placeholders, consider displaying a loading indicator.

import { LoadingProvider } from '@shopgate/pwa-common/providers';

LoadingProvider.setLoading("/your-current-page-pathname");
LoadingProvider.unsetLoading("/your-current-page-pathname");