Overriding components

Edit on GitHub

If the implementation of any of the components shipped with Spryker Frontend does not suite your needs, you can override it with a component of your own. The following article shows how to override a molecule called simple-carousel. By default, the component is used, for example, to display product suggestions at the bottom of the page.

Old simple carousel

Let us replace the default implementation of this molecule with our own version. It will look basically the same as the default one, but we’ll use a different color for the left and right arrows; the current view number will be displayed in the browser console. Also, when reaching the last image in the set, the component will no longer be reset to the first view automatically by pressing the right button.

1. Create component folder on project level

On the global level, the molecule we want to override is located in the following folder: vendor/spryker-shop/shop-ui/src/SprykerShop/Yves/ShopUi/Theme/default/components/molecules/simple-carousel. It is a part of the main Shop UI application. To override it, we need to place our implementation in the Shop UI folder on the project level, and also replicate the folder structure. In other words, we need to create the following folder for the component: src/Pyz/Yves/ShopUi/Theme/default/components/molecules/simple-carousel.

Also, we need to create an entry point for Webpack. Create an empty index.ts file in the folder.

2. Override component template

The first thing we need to do is to override the Twig of the default component. To do this, we need to create a Twig file for the component on the project level. Since the project level has priority over the global level, the newly created project-side Twig will override the global one. In other words, whenever the component is used on a page, your implementation will be used instead of the default one.

To override the global component, the Twig file name on the project level must be the same as on the global level. Also, component name and tag name specified in the config property must be the same as on the global level. In our case, the file name on the project level must be simple-carousel.twig, and the config property will look as follows:

{% define config = {
    name: 'simple-carousel',
    tag: 'simple-carousel'
} %}

Since the new component will be used everywhere instead of the global one, it is also recommended to replicate the data contracts. For this purpose, the data and attributes properties need to be configured the same as in the source component. If you are going to change the outlook of the component only, without changing its data property, attributes etc, you can copy the source Twig file, and then make changes to the body block only. The block contains the visual layout of a component.

As we are not going to change the component template, let us copy the whole of the source twig implementation (vendor/spryker-shop/shop-ui/src/SprykerShop/Yves/ShopUi/Theme/default/components/molecules/simple-carousel/simple-carousel.twig) to the project level (src/Pyz/Yves/ShopUi/Theme/default/components/molecules/simple-carousel.twig). The Twig file will look as follows:

simple-carousel.twig
{% extends model('component') %}

{% define config = {
    name: 'simple-carousel',
    tag: 'simple-carousel'
} %}

{% define data = {
    slides: [],
    showDots: true
} %}

{% define attributes = {
    'slides-to-show': 1,
    'slides-to-scroll': 1
} %}

{% set slidesCount = data.slides | length %}
{% set slideWidth = 100 / attributes['slides-to-show'] %}
{% set dotsCount = (((slidesCount - attributes['slides-to-show']) / attributes['slides-to-scroll']) | round(0, 'ceil')) + 1 %}
{% set showNavigation = data.slides | length > 1 %}
{% set showDots = data.showDots and dotsCount > 1 %}

{#
    Insert customizations in the body of the component, if necessary
#}
{% block body %}
    <div class="{{config.name}}__container">
        {% if showNavigation %}
            <div class="{{config.name}}__navigation {{config.name}}__navigation--prev">
                <a href="#prev" class="{{config.name}}__arrow {{config.jsName}}__prev">
                    {% include atom('icon') with {
                        modifiers: ['big'],
                        data: {
                            name: 'chevron-left'
                        }
                    } only %}
                </a>
        </div>
        {% endif %}

        <div class="{{config.name}}__view">
            <div class="{{config.name}}__slider {{config.jsName}}__slider grid grid--stretch">
                {% for slide in data.slides %}
                    <div class="{{config.jsName}}__slide col" style="width:{{slideWidth}}%;min-width:{{slideWidth}}%;">
                        {% block slide %}{% endblock %}
                    </div>
                {% endfor %}
            </div>
            {% if showDots %}
                <div class="{{config.name}}__dots">
                    {%- for dot in 1..dotsCount -%}
                        <a href="#" class="{{config.name}}__dot {{config.jsName}}__dot{% if loop.first %} {{config.name}}__dot--current{% endif %}"></a>
                    {%- endfor -%}
                </div>
            {% endif %}
        </div>

        {% if showNavigation %}
            <div class="{{config.name}}__navigation {{config.name}}__navigation--next">
                <a href="#next" class="{{config.name}}__arrow {{config.jsName}}__next">
                    {% include atom('icon') with {
                        modifiers: ['big'],
                        data: {
                            name: 'chevron-right'
                        }
                    } only %}
                </a>
        </div>
        {% endif %}
    </div>
{% endblock %}

3. Define component styles

The next thing to do is to provide the styles to use in the component. We need to use the mixin of the default component and add our style customization within the mixin. For this purpose, create file simple-carousel.scss in the project folder with the following content:

@include shop-ui-simple-carousel {
    color: $setting-color-alt;
}

With the above code, we inherit all styles of the default component, and then override the arrow color with another one. The color is taken from global configuration. It can be found in the followig folder: vendor/spryker-shop/shop-ui/src/SprykerShop/Yves/ShopUi/Theme/default/styles/settings.

Now, we need to make the style visible for Webpack using the index.ts file:

// Import component style
import './simple-carousel.scss';

4. Change component behavior

Finally, let us define what the component does. Create the simple-carousel.ts file with the following content:

// Import the SimpleCarousel class
import SimpleCarousel from 'ShopUi/components/molecules/simple-carousel/simple-carousel';

// Export the extended class with additional behavior
export default class SimpleCarouselExtended extends SimpleCarousel {
    slide(): void {
        console.log(`Showing view number ${this.viewCurrentIndex + 1}/${this.viewsCount}!`);

        super.slide();

        // If we've reached the last image, prevent automatic transfer to the 1st one
        if (this.viewCurrentIndex + 1 === this.viewsCount) {
            this.viewCurrentIndex = this.viewCurrentIndex -1;
        }
    }
}

In the above example, first, we import class SimpleCarousel from the global level. After that, we declare and export a new class, SimpleCarouselExtended. Since it extends the class of the default component, it inherits all of its its behavior. The code contained inside the class adds behavior on its own.

If you want to define the component behavior from scratch rather than importing the behavior of the default component, you need to extend the base Component class instead:

import Component from 'ShopUi/models/component';

export default class SimpleCarouselExtended extends Component {
         // TODO: your code here
}

After implementing the component behavior, let us bond it to the DOM element that represents the component. We need to use the same tag name as the default component, in our case, simple-carousel. Open the index.ts file again and add the following content:

// Import the 'register' function from the Shop Application
import register from 'ShopUi/app/registry';

// Register the component
export default register(
    'simple-carousel',
    () => import(/* webpackMode: "lazy" */'./simple-carousel')
);

5. Build frontend

Now, let us build the frontend. Run the following command in the console: npm run yves.

As soon as npm finishes, you can see the changes in the simple carousel component. The arrow color is changed and you cannot switch to the 1st element after reaching the last one:

New simple carousel

If you open the browser console, you can see the current view number each time you press the left or the right arrow.

Carousel console