This guide can be used for any theme development involving the Experro base-theme, as well as for customizing the base-theme through the Experro platform.

Basic Rules

  • Ensure each file contains only one component.
  • Utilize JSX syntax consistently for every component.
  • Opt for TypeScript over JavaScript, particularly for new code, whenever feasible.

File Creation

  • Adhere to kebab-case when creating any files.
  • Always generate component files with the extension .tsx.
  • Create controller files with the extension .ts exclusively.
//bad
Exp - hero_banner.tsx;
Exp - hero_banner - controller.tsx;

//good
exp - hero_banner.tsx;
exp - hero - banner - controller.ts;

CMS Component Creation

  1. Component Folder:
    • Within the cms-library directory, create a new folder named after your theme component. This folder will store all the component’s files.
  2. Component Structure:
    • Inside the component folder:
      • Create a file with the same name as your component but with the .tsx extension. This file is responsible for returning the component’s JSX code.
      • Create another file with the .ts extension. This file will contain all the business logic for your component, separate from the JSX.
      • Create an index.ts file. This file will act as the entry point, potentially exporting the main component and any helper functions from the controller file.
  3. Register the Component (parent index.ts):
    • In the parent folder of cms-library (likely named components), locate the index.ts file.
    • Edit this index.ts file to include an entry for your newly created component. This typically involves importing the component from its folder location and adding it to an export statement.
By following these steps, you’ll create a well-organized component with a clear separation of concerns and ensure it’s registered for use within the cms-library.

Props

  • Always use camelCase for prop names, or snake-case if the prop value is a React component.
// bad
<ExpBanner
  TitleText="hello"
  DescriptionText="This is description"
/>

// good
<ExpBanner
  titleText="hello"
  description_text="This is description"
/>

Parentheses

Wrap JSX tags in parentheses when they span more than one line. eslint: react/jsx-wrap-multilines
//bad
return <ExpHeroBanner varriant='layout-1'>
        <ExpHeroBanner>

//good
return (
        <ExpHeroBanner varriant='layout-1'/>
    );

Method

Use Arrow functions to close over local variables. For repetitive tasks or functions with identical logic declared and invoked in multiple components, consider relocating them to the utils folder and exporting them from its index. Do not use underscore prefix for internal methods of a React component.
//bad
const _getProductData = () => {...};

//good
const getProductData = () => {...};

Don’t use underscores for privacy in JavaScript components. While some

languages use underscores to mark something as private, JavaScript doesn’t have built-in privacy. All properties, even those with underscores, are accessible by anyone using your code. Treat all component properties as public and use other methods (like closures or modules) for true data encapsulation if needed.

Import only top-level modules

The files inside a module are implementation details of that module. They should never be imported directly. Instead, you must only import the top-level API that’s exported by the module itself. On the other hand, a module should be able to import parent and sibling modules.
// bad
import ExpHeroBanner from "./components/hero-banner/exp-hero-banner";
import inSibling from "../components/child";

// good
import components from "./components";
import ExpHeroBanner from "./components/hero-banner";
import parent from "../";
import ancestor from "../../../";
import sibling from "../components";

Avoid export * in top level index.ts files

The exports in componetns/index.ts, public/index.ts dictate a plugin’s public API. The public API should be carefully controlled, and using export * makes it very easy for a developer working on internal changes to export a new public API unintentionally:
// bad
export * from "foo/child";
export * from "../foo/child";

// good
export { ExpHeroBanner } from "./component/hero-banner";
export { child } from "./child";

Write small functions

Keep your functions short. A good function fits on a slide that the people in the last row of a big room can comfortably read. So don’t count on them having perfect vision and limit yourself to ~15 lines of code per function.

Default argument syntax

Always use the default argument syntax for optional arguments:
// bad
function validationCheck(fields, isSubmit) {
  if (typeof fields === 'undefined') {
    fields = [];
  }
  ...
}

// good
function validationCheck(fields = [], isSubmit = false) {
  ...
}

Prettier and Linting

We are gradually moving the Experro theme code base over to Prettier. All TypeScript code and some JavaScript code (check .eslintrc.js) is using Prettier to format code. We recommend you to enable running ESLint via your IDE. Whenever possible we are trying to use Prettier and linting, instead of maintaining a set of written style guide rules. Consider every linting rule and every Prettier rule to be also part of our style guide and disable them only in exceptional cases and ideally leave a comment why they are disabled at that specific place.

Avoid any whenever possible

With the advent of TypeScript 3.0 and the introduction of the unknown type, there are seldom reasons to employ any as a type. Nearly all instances where any was previously used can be substituted with either a generic or unknown type (in cases where the type is genuinely unknown). It’s advisable to consistently utilize these mechanisms over any, as they offer stricter typing and are less prone to introducing bugs in the future due to inadequate types. If your plugin does not utilize any or if you’re initiating a new plugin, it’s recommended to enable the @typescript-eslint/no-explicit-any linting rule for your plugin via the .eslintrc.js configuration.

Use slashes for comments

Use slashes for both single line and multi line comments. Try to write comments that explain higher level mechanisms or clarify difficult segments of your code. Don’t use comments to restate trivial things.
// bad

// Execute a regex
const matches = item.match(/ID_([^\n]+)=([^\n]+)/));

// Usage: loadUser(5, function() { ... })
function loadUser(id, cb) {
  // ...
}

// Check if the session is valid
const isSessionValid = (session.expires < Date.now());
// If the session is valid
if (isSessionValid) {
  ...
}

// good

// 'ID_SOMETHING=VALUE' -> ['ID_SOMETHING=VALUE', 'SOMETHING', 'VALUE']
const matches = item.match(/ID_([^\n]+)=([^\n]+)/));

/**
 * Fetches a user from...
 * @param  {string} id - id of the user
 * @return {Promise}
 */
function loadUser(id) {
  // This function has a nasty side effect where a failure to increment a
  // redis counter used for statistics will cause an exception. This needs
  // to be fixed in a later iteration.

  ...
}

const isSessionValid = (session.expires < Date.now());
if (isSessionValid) {
  ...
}

SASS files

When writing a new component, create a sibling SASS file of the same name and import directly into the top of the JS/TS component file. Doing so ensures the styles are never separated or lost on import and allows for better modularization (smaller individual plugin asset footprint). It is recommended to create a new file for component or file in that particuler comonents folder. and import it in a src/assets/scss/app.scss for global mapping.