Stepper Component with RiotJS (Material Design)
Tags: riotjs, frontend, javascript, beginners
Published: 2024-04-08 18:40:51

This article covers creating a Riot Stepper component, using the Material Design CSS BeerCSS, and change the step when a button is clicked.

Before starting, make sure you have a base application running, or read my previous article Setup [Riot + BeerCSS + Vite](https://dev.to/steeve/riotjs-material-design-how-to-setup-beercss-269c).

> These articles form a series focusing on [RiotJS](https://riot.js.org/) paired with [BeerCSS](https://www.beercss.com/), designed to guide you through creating components and mastering best practices for building production-ready applications. I assume you have a foundational understanding of Riot; however, feel free to refer to the documentation if needed: https://riot.js.org/documentation/

Steppers display progress through a multi-step process. Users intuitively know where they are in the process and how many steps remain, for instance, a checkout flow, login, or a form.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88kakb2uj4y1j1lymaz9.png)

## Stepper Component Base

The goal is to create a Riot app with a Stepper that shows a progression when a button is clicked and prints the corresponding page. 

![GIF of a Stepper made with RiotJS and BeerCSS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wy5bskjv4033k30wbh3v.gif)

Open the **index.riot** file at the root of your Vite project, then add the following code. The HTML comes from the [BeerCSS documentation](https://www.beercss.com/#steppers) and I added [RiotJS syntax](https://riot.js.org/documentation/#syntax) for the logic:
```html
<index-riot>
    <div style="width:800px;padding:20px;">
        <nav class="scroll">
            <button class="circle small"  onclick={ () => changeStep(1)}>
                <template if={ state.active === 1 }>1</template>
                <i if={ state.active > 1}>done</i>
            </button>
            <div class="max divider"></div>
            <button class="circle small" disabled={ state.active < 2 } onclick={ () => changeStep(2)}>
                <template if={ state.active <= 2 }>2</template>
                <i if={ state.active > 2}>done</i>
            </button>
            <div class="max divider"></div>
            <button class="circle small" disabled={ state.active < 3 } onclick={ () => changeStep(3)}>
                <template if={ state.active <= 3 }>3</template>
                <i if={ state.active > 3}>done</i>
            </button>
        </nav>
        <br>
        <div class="page padding { state.active === 1 ? 'active' : null}" if={ state.active === 1 }>
            <h5>Page { state.active }</h5><br>
            <button onclick={ () => { changeStep(2) }}>Next</button>
        </div>
        <div class="page padding { state.active === 2 ? 'active' : null}" if={ state.active === 2 }>
            <h5>Page { state.active }</h5><br>
            <button onclick={ () => { changeStep(3) }}>Next</button>
        </div>
        <div class="page padding { state.active === 3 ? 'active' : null}" if={ state.active === 3 }>
            <h5>Page { state.active }</h5><br>
            <button onclick={ () => { changeStep(4) }}>Next</button>
        </div>
        <div class="page padding { state.active === 4 ? 'active' : null}" if={ state.active === 4 }>
            <h5>Done!</h5>
        </div>
    </div>
    <script>
        export default {
            state: {
                active: 1,
            },
            changeStep(value) {
                this.update({
                    active: value
                })
            }
        }
    </script>
</index-riot>
```
> Source Code: https://github.com/steevepay/riot-beercss/blob/main/examples/index.stepper.riot

Let's break down the code:
1. The Stepper is made of Buttons and Dividers, the state of a step is defined by:
  * **Completed:** when the checked icon is displayed and the number is hidden with `<template if={ state.active === 1 }>1</template>`.
  * **Completing:** when the number is displayed and the checked icon is hidden with `<i if={ state.active > 1}>done</i>`.
  * **Next Step upcoming:** when the button is disabled with the condition `disabled={ state.active < 2 }`
2. The state of the Stepper is stored into a **state:{}** Riot Object under the **state.active** value.
3. The **state.active** will print pages 1, 2 or 3 with the expression `<div class="page padding" if={ state.active === 1 }>`.
4. If one of the step buttons is clicked, it will directly update **state.active** with the selected step number: The function `changeStep` is fired when the event "Click" is caught, such as: `onclick={ () => changeStep(1)}`. Changing the step can be used to request an API and then load a specific page.


## Stepper Component Testing

It exists two methods for testing the Stepper component, and it is covered in two different articles:
* [Test with Vitest and Riot-SSR in a Node Environment](https://dev.to/steeve/riot-component-unit-test-with-vitest-node-env-hk0)
* [Test with Vitest in a JsDom Environment](https://dev.to/steeve/riot-component-unit-test-with-vitest-jsdom-env-182l)

## Conclusion

Voilà 🎉 We made a Stepper Riot Component using Material Design elements with BeerCSS.

Feel free to comment if you have questions or need help with RiotJS.

Have a great day! Cheers 🍻