Chip Component with RiotJS (Material Design)

Steeve - Mar 31 - - Dev Community

This article covers creating a Riot Chip 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.

These articles form a series focusing on RiotJS paired with BeerCSS, 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/

Chips help people enter information, make selections, filter content, or trigger actions (see the following screenshot). The goal is to create a Chip Riot component that supports all styling and listens to click events.

Chips example from the material.io documentation

Chip Component Base

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

Into ./components/c-chip.riot, write the following HTML (found in the BeerCSS documentation):

<c-chip>
    <a class="
            chip
            {props?.small ? 'small' : null }
            {props?.medium ? 'medium' : null }
            {props?.large ? 'large' : null }

            {props?.smallElevate ? 'small-elevate' : null}
            {props?.mediumElevate ? 'medium-elevate' : null}
            {props?.largeElevate ? 'large-elevate' : null}

            {props?.round ? 'round' : null }
            {props?.fill ? 'fill' : null }
            {props?.vertical ? 'vertical' : null }
        ">
        <i if={ props?.icon }>{ props.icon }</i>
        <img class={props?.inline ? null : "responsive" } if={ props?.img } src={ props.img }>
        <span><slot></slot></span>
        <i if={ props?.iconend }>{ props.iconend }</i>
    </a>
</c-chip>
Enter fullscreen mode Exit fullscreen mode

Let's break down the code:

  1. The <c-chip> and </c-chip> defined a custom root tag, with the same name as the file. You must write it; otherwise, it may create unexpected results. Using the <a> as a root tag or redefining native HTML tags is a bad practice, so starting c- is a good naming.
  2. Using the <slot> tag you can inject custom HTML templates in a child component from its parent: we will inject the chip's label.
  3. A chip has many variations classes: size, elevation and form (round, fill or vertical). It is injected conditionally if the corresponding props exist. For instance, if the property props.round exists, it will enable the round class in the component.
  4. Two icons can be passed at two positions: either as a prefix or as a suffix of the label:
    • the property icon injects the glyph before the label
    • the property iconend injects the glyph after the label
  5. Thanks to the img property, an image can be injected instead of an icon.

Finally, the c-chip.riot can be instantiated into a front page index.riot. Here are all Chip variations, either filled, rounded, including an image or icon:

<index-riot>
    <div style="width:800px;padding:20px;">
        <h4 style="margin-bottom:20px;margin-left:10px;">Riot + BeerCSS</h4>
        <h6 style="margin-left:10px">Chip Variations</h6><br>
        <c-chip>Suggestion</c-chip>
        <c-chip iconend="close">Input</c-chip>
        <c-chip icon="check">filter</c-chip>
        <c-chip icon="today">assist</c-chip>
        <c-chip img="./favicon.png" inline="true">image</c-chip>
        <c-chip img="./favicon.png">image</c-chip>
        <br><br>
        <c-chip round="true">Suggestion</c-chip>
        <c-chip round="true" iconend="close">Input</c-chip>
        <c-chip round="true" icon="check">filter</c-chip>
        <c-chip round="true" icon="today">assist</c-chip>
        <c-chip round="true" img="./favicon.png" inline="true">image</c-chip>
        <c-chip round="true" img="./favicon.png">image</c-chip>
        <br><br>
        <c-chip fill="true">Suggestion</c-chip>
        <c-chip fill="true" iconend="close">Input</c-chip>
        <c-chip fill="true" icon="check">filter</c-chip>
        <c-chip fill="true" icon="today">assist</c-chip>
        <c-chip fill="true" img="./favicon.png" inline="true">image</c-chip>
        <c-chip fill="true" img="./favicon.png">image</c-chip><br><br>
        <c-chip fill="true" round="true">Suggestion</c-chip>
        <c-chip fill="true" round="true" iconend="close">Input</c-chip>
        <c-chip fill="true" round="true" icon="check">filter</c-chip>
        <c-chip fill="true" round="true" icon="today">assist</c-chip>
        <c-chip fill="true" round="true" img="./favicon.png" inline="true">image</c-chip>
        <c-chip fill="true" round="true" img="./favicon.png">image</c-chip>
    </div>
    <script>
        import cChip from "./components/c-chip.riot"

        export default {
            components: {
                cChip
            },
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

Code break-down:

  1. The component is imported with import cChip from "./components/c-chip.riot"; then loaded into the components:{} Riot object.
  2. On the HTML, the Chip component is instantiated with <c-chip>label<c-chip>
  3. Pass attributes to the tag to enable variations, for instance: <c-chip fill="true" round="true" icon="check">filter</c-chip>.

Screenshot of the generated HTML:
Variations of the chip Component made with RiotJS and BeerCSS

Chip Component Events

As mentioned, a chip is used to filter, suggest and trigger actions. A real-world example would be that you must select your flat's amenities for a rental application.

Let's make a group of chips for each amenity and check if it is selected.

Chip group listening to click events

Here is the corresponding HTML and Riot code in the index.riot:

<index-riot>
    <div style="width:800px;padding:20px;">
        <h6 style="margin-left:10px">Amenities</h6><br>
        <c-chip each={ el in state.amenities } onclick={ (ev) => clicked(el.label) } fill={ el.checked === true } icon={ el.checked === true ? 'check' : null }> { el.label } </c-chip>
    </div>
    <script>
        import cChip from "./components/c-chip.riot"

        export default {
            components: {
                cChip
            },
            state: {
                amenities: [
                    { label: "Wash / Dryer", checked: false },
                    { label: "Ramp Access", checked: false },
                    { label: "Garden", checked: false },
                    { label: "Cat OK", checked: false }
                ]
            },
            clicked (label) {
                const _el = this.state.amenities.find(el => el.label === label);
                if (_el) {
                    _el.checked = !_el.checked;
                    this.update();
                }
            }
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

Code details:

  1. A list of Chips is printed thanks to the Each Riot attribute.
  2. Each chip is listening to a click event with onclick={ (ev) => clicked(el.label) }. If a click event is fired, the function clicked is executed, and the label is passed as first argument.
  3. The clicked function finds the list item based on the label, then the checked property is updated.
  4. When the checked is true, two attributes are enabled fill and icon.

Chip Component Testing

It exists two methods for testing the Chip component, and it is covered in two different articles:

Conclusion

Voilà 🎉 We created a Chip Riot Component using Material Design elements with BeerCSS.

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

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

Have a great day! Cheers 🍻

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .