Adding a Section in My Account

Learn to override with a common pattern in Scandi – render maps

In Scandi, several UI elements, including the My Account page, are organized in tabs:

In this tutorial, we will learn to customize the theme by adding a new tab to the My Account page. If you follow along with this tutorial, you should be able to add any component to any tab in Scandi.

Creating a New Component

First, we need to create a component responsible for displaying the content of the tab. This means creating a new subdirectory in the component directory with the required files. Using the scandipwa CLI utility, this step can be automated with a single command.

We will name our example component PositiveAffirmations:

scandipwa create component PositiveAffirmations

This should create some new files and give the following output:

NOTE!

     The following files have been created:
     src/component/PositiveAffirmations/PositiveAffirmations.component.js
     src/component/PositiveAffirmations/PositiveAffirmations.style.scss
     src/component/PositiveAffirmations/index.js

Now, let's edit the .component.js file to render some example content. For simplicity, our example will contain positive self-affirmations copy-pasted from the internet:

src/component/PositiveAffirmations/PositiveAffirmations.component.js
    render() {
        return (
            <div block="PositiveAffirmations">
                <p>{ __('Today, I am brimming with energy and overflowing with joy.') }</p>
                <p>{ __('I live in the moment while learning from the past and preparing for the future.') }</p>
                <p>{ __('Life is beautiful.') }</p>
            </div>
        );
    }

This will help customers gain confidence and boost their mood while they adjusting the settings for their account.

Wondering what the { __("") } magic is for? They are necessary to enable translating the text. You could remove them, and the app would still work, but only in English. Let's break down what that syntax does:

  1. The curly braces {} allow us to escape from JSX (that HTML-like syntax) and write normal JavaScript expressions.

  2. __ is a special function-like directive in Scandi. It allows us to translate the content.

  3. The text needs to be put in quotes "" so that it can be processed like any other JavaScript string.

Great! Now we have created the component, but it's not yet visible on the page, because we haven't used it anywhere – that's the next step.

Inspecting the Codebase

To add a new tab, we need to find out which component is responsible for rendering all of the My Account tabs. Using React Developer tools, we can identify the MyAccountTabList component:

Inspecting the source code and searching for usages of <MyAccountTabList, we find that the tabs are passed as props from MyAccount.component.js

node_modules/@scandipwa/scandipwa/src/route/MyAccount/MyAccount.component.js
    renderContent() {
        const {
            tabMap,
	          // ...
        } = this.props;
	      // ...

	const TabContent = this.renderMap[activeTab];
        const { name } = tabMap[activeTab];

        return (
            <ContentWrapper>
                <MyAccountTabList
                  tabMap={ tabMap }
                  activeTab={ activeTab }
                  changeActiveTab={ changeActiveTab }
                  onSignOut={ onSignOut }
                />
		            <div block="MyAccount" elem="TabContent">
                    <h2 block="MyAccount" elem="Heading">{ name }</h2>
                    <TabContent isEditingActive={ isEditingActive } />
                </div>
            </ContentWrapper>
        );
    }

The tab components themselves are specified in the renderMap:

node_modules/@scandipwa/scandipwa/src/route/MyAccount/MyAccount.component.js
    renderMap = {
        [DASHBOARD]: MyAccountDashboard,
        [MY_ORDERS]: MyAccountMyOrders,
        [MY_WISHLIST]: MyAccountMyWishlist,
        [ADDRESS_BOOK]: MyAccountAddressBook,
        [NEWSLETTER_SUBSCRIPTION]: MyAccountNewsletterSubscription,
        [MY_DOWNLOADABLE]: MyAccountDownloadable
    };

The metadata (such as the tab name), is passed to the component as a prop from MyAccount.container.js, where the tabMap is defined:

node_modules/@scandipwa/scandipwa/src/route/MyAccount/MyAccount.container.js
    tabMap = {
        [DASHBOARD]: {
            url: '/dashboard',
            name: __('Dashboard')
        },
        [ADDRESS_BOOK]: {
            url: '/address-book',
            name: __('Address book')
        },
        [MY_ORDERS]: {
            url: '/my-orders',
            name: __('My orders')
        },
        [MY_DOWNLOADABLE]: {
            url: '/my-downloadable',
            name: __('My downloadable')
        },
        [MY_WISHLIST]: {
            url: '/my-wishlist',
            name: __('My wishlist'),
            headerTitle: () => this.getMyWishlistHeaderTitle()
        },
        [NEWSLETTER_SUBSCRIPTION]: {
            url: '/newsletter-subscription',
            name: __('Newsletter Subscription')
        }
    };

We need to override the .container to add a new tab entry, but we also need to override the .component to specify how to render it.

Adding a New Tab

As we found above, we need to override the MyAccount route to customize the tabs. Again, the scandipwa CLI can do this automatically for us. First, enter this command:

scandipwa override route MyAccount

Then, the scandipwa CLI will interactively ask you which parts of the MyAccount route you want to override. We only want to override the MyAccountContainer class in the MyAccount.container.js file, and the MyAccount class in the .component file; leave the other answers blank.

? Choose things to extend in MyAccount.component.js MyAccount
? What would you like to do with styles? Keep
? Choose things to extend in MyAccount.config.js 
? Choose things to extend in MyAccount.container.js MyAccountContainer

NOTE!

     The following files have been created:
     src/route/MyAccount/MyAccount.component.js
     src/route/MyAccount/MyAccount.container.js

The command will automatically create the boilerplate necessary for overriding the file. Now, it's up to us to make the necessary changes in these files.

First, open the MyAccount.component file and configure the renderMap to include our custom component.

src/route/MyAccount/MyAccount.component.js
export const POSITIVE_AFFIRMATIONS = 'affirmations';

/** @namespace TutorialCsaApp/Route/MyAccount/Component/MyAccountComponent */
export class MyAccountComponent extends SourceMyAccount {
    renderMap = {
        [DASHBOARD]: MyAccountDashboard,
        [MY_ORDERS]: MyAccountMyOrders,
        [MY_WISHLIST]: MyAccountMyWishlist,
        [ADDRESS_BOOK]: MyAccountAddressBook,
        [NEWSLETTER_SUBSCRIPTION]: MyAccountNewsletterSubscription,
        [MY_DOWNLOADABLE]: MyAccountDownloadable,
        [POSITIVE_AFFIRMATIONS]: PositiveAffirmations // <-- added!
    };
}

Now, MyAccountComponent knows how to render our custom tab. We still need to configure the tab URL and title for it to appear in the list. For this, we can override the tabMap field of the MyAccountContainer.

src/route/MyAccount/MyAccount.container.js
import { POSITIVE_AFFIRMATIONS } from './MyAccount.component';

/** @namespace TutorialCsaApp/Route/MyAccount/Container/MyAccountContainer */
export class MyAccountContainer extends SourceMyAccountContainer {
    tabMap = {
        [DASHBOARD]: {
            url: '/dashboard',
            name: __('Dashboard')
        },
        // [...] copy all the original entries from MyAccountContainer.tabMap here
	// (unless you want to intentionally remove them)

        [POSITIVE_AFFIRMATIONS]: { // <-- new entry - custom tab!
            url: '/affirmations',
            name: __('Positive Affirmations')
        }
    };
}

Scandi is very strict about code style, and you might have to fix some issues for the ESLint check to pass. Most issues can be automatically fixed by running eslint --fix src.

Result

Let's check the brand-new My Account tab – as expected, it appears among the other tabs!

Congrats! You have now learned to add new tabs to Scandi components. And you will find that similar patterns with renderMap can be overridden in the same way – you can add, remove, or modify which components are rendered by configuring an object or array.

Last updated