Radio Component with RiotJS (Material Design)
Tags: riotjs, frontend, javascript, beginners
Published: 2024-03-27 06:46:30

This article covers creating a Riot Radio component, using the Material Design CSS BeerCSS. 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/

**A Radio is often part of a group of Radio buttons:** Only one radio button in a given group can be selected simultaneously (see the following screenshot). The goal is to create a Radio component with BeerCSS design and listen to change events.

![Radio elements from the BeerCSS documentation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0x5ei5v2c43vk4uzrvp.png)

<u>Note: The article doesn't cover how to create a radio as an Icon or as an Image.</u>

## Radio Component Base

First, create a new file named `c-radio.riot` under the components folder. The `c-` stands for "component", a useful naming convention and a good practice.

Write the following HTML code (found on the BeerCSS documentation) in `./components/c-radio.riot`:
```html
<c-radio >
    <label class="radio">
        <input type="radio" name={ props?.name } value={ props?.defaultValue } checked={ props?.defaultValue === props?.value } disabled={ props?.disabled }>
        <span>
            <slot></slot>
        </span>
    </label>
</c-radio>
```
Let's break down the code:
* The `<c-radio>` and `</c-radio>` defined a custom root tag, with the same name as the file. You must write it; otherwise, it may create unexpected results. Using the `<label>` as a root tag or redefining native HTML tags is a bad practice, so starting `c-` is a good naming.
* A **name** attribute is required for each Radio: Radios of the same group must have the same **name**.
* The radio takes two different values attributes:
   * The **default-value** attribute defines the radio value.
   * the **value** attribute defines the value of the group.
* To enable the `checked` attribute, the **value** of the group must be equal to the radio default value **default-value**.
* A radio can have a label; it must be passed as a [Slot](https://riot.js.org/documentation/#slots), a Riot tag to inject HTML templates in a child component from its parent.
* The element is disabled if the `props.disabled` attribute exists and the value is `true`.

Now to create a radio group, let's create a parent component named **./components/c-radio-group.riot**. The following HTML was copied from the BeerCSS documentation, and RiotJS logic was added:
```html
<c-radio-group class="field middle-align">
    <nav>
        <slot></slot>
    </nav>
    <span class="helper" if={ props?.helper && !props?.error }>{ props.helper} </span>
    <span class="error" if={ props?.error }>{ props.error}</span>
</c-radio-group>
```
* To define multiple Radio components in the group, Radios are injected thanks to the `<slot></slot>` tag: The [Slot tag]([Slot](https://riot.js.org/documentation/#slots)) is a special Riot.js core feature that allows you to inject and compile the content of any custom component.
* The helper span is displayed if the **helper** attribute exists.
* If the **error** attribute exists, the error span is displayed and the helper is hidden.

Finally, load and instantiate the **c-radio.riot** and **c-radio-group.riot**, in a front page named **index.riot**:
```html
<index-riot>
    <div style="width:600px;padding:20px;">
        <h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
        <c-radio-group oninput={ changed }>
            <c-radio default-value="blue" value={ state.value } name="color">Blue</c-radio>
            <c-radio default-value="red" value={ state.value } name="color">Red</c-radio>
            <c-radio default-value="green" value={ state.value } onclick={ clicked } name="color">Green</c-radio>
        </c-radio-group>
        Selected: { state.value }
    </div>
    <script>
        import cRadio from "./components/c-radio.riot";
        import cRadioGroup from "./components/c-radio-group.riot"

        export default {
            components: {
                cRadio,
                cRadioGroup
            },
            state: {
                value: "green"
            },
            changed (ev) {
                this.update({ value: ev.target.value })
            }
        }
    </script>
</index-riot>
```
Code details:
1. Components are imported with `import cRadio from "./components/c-radio.riot";` and `import cRadioGroup from "./components/c-radio-group.riot"` then loaded in the **components:{}** Riot object.
2. The component RadioGroup is instantiated with `<c-radio-group></c-radio-group>` on the HTML.
3. The component Radio is instantiated within the Radio group as a Slot, such as: `<c-radio-group><c-radio>label<c-radio></c-radio-group>`.
4. Each Radio gets the same group name: **color**.
5. Each Radio gets a default value with the attributes name **default-value**: red, green or blue.
6. The state of the radio group is stored in the state Riot object `state: { value: 'green' }`. **Green** is the default value.
7. If a click is made on the Radio group, the `change` event is fired, and the `changed` function is executed to update the value of the group *state.value*.
8. To update a state, you must use the [update()](https://riot.js.org/api/#componentupdate) Riot function, such as: `this.update({ value: ev.target.value })`. The **state.value** gets the Radio change event value.

Here is the generated HTML:

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


## Radio Component Testing

It exists two methods for testing the Radio 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 created a Radio Riot Component using Material Design elements with BeerCSS. 

The source code of the radio is available on Github:
https://github.com/steevepay/riot-beercss/blob/main/components/c-radio.riot

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

Have a great day! Cheers 🍻