Dialog Component with RiotJS (Material Design)

Steeve - Apr 2 - - Dev Community

This article covers creating a Riot Dialog component, using the Material Design CSS BeerCSS, and reacting to click events.

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/

The Dialog, informs users about a specific task and may contain critical information, require decisions, or involve multiple tasks. In a modal dialog, the user is interrupted and interacts with the dialog before they can continue interacting with the rest of the application, for instance choosing a phone ringtone:

Modal example to select a phone ringtone

Dialog Component Base

The goal is to create a Riot app with a Dialog and execute an action if the modal is cancelled or confirmed.

Display a modal when a button is clicked and close the modal when the confirm or cancel button is clicked

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

Write the following HTML code (found on the BeerCSS documentation) in ./components/c-dialog.riot:

<c-dialog>
    <dialog class="
                {props?.active ? 'active ' : null}
                {props?.blur ? 'overlay blur ' : null}
                {props?.left ? 'left ' : null}
                {props?.right ? 'right ' : null}
                {props?.top ? 'top ' : null}
                {props?.bottom ? 'bottom ' : null}
                {props?.max ? 'max ' : null}
            "
        >
        <h5 if={ props?.title }>{ props?.title }</h5>
        <div>
            { props?.message }
            <slot name="body"></slot>
        </div>
        <nav class="right-align no-space">
            <button onclick={ (ev) => clicked(ev, "cancel") } class="transparent link" if={ !props?.disableCancel }>{ props?.cancelText ?? "cancel" }</button>
            <button onclick={ (ev) => clicked(ev, "confirm") } class="transparent link" if={ !props?.disableConfirm }>{ props?.confirmText ?? "confirm" }</button>
            <slot name="button"></slot>
        </nav>
    </dialog>
       <script>
        export default {
            clicked (e, name) {
                e.preventDefault();
                e.stopPropagation();
                this.root.dispatchEvent(new Event(name));
            }
        }
    </script>
</c-dialog>
Enter fullscreen mode Exit fullscreen mode

Source Code: https://github.com/steevepay/riot-beercss/blob/main/components/c-dialog.riot

Let's break down the code:

  1. The <c-dialog> and </c-dialog> define a custom root tag, with the same name as the file. You must write it; otherwise, it may create unexpected results. Using the <dialog></dialog> as a root tag or redefining native HTML tags is a bad practice, so starting c- is a good naming.
  2. The Dialog has three main sections:
    • A title as header filled thanks to the {props.title}.
    • A message as body printed with {props.message}.
    • A call to action with two buttons: cancel and confirm.
  3. When a button is clicked, the click Event is caught into the clicked function, and then a Custom Event is emitted to the parent HTML with this.root.dispatchEvent(new Event(name));: it will emit either confirm or cancel.
  4. The modal is displayed only if the props.active exists and is positive; then, the class active is applied.
  5. The Dialog component has different styles, and it is printed conditionally if their corresponding props exist, for making the Dialog fullscreen: {props?.max ? 'max ' : null}: if the property max exists, the class max is applied.

Finally, load and instantiate the c-dialog.riot in a front page named index.riot:

<index-riot>
    <div style="width:800px;padding:20px;">
        <c-button onclick={ (ev) => update({ active: !state.active, max: false })}>Dialog</c-button> 
        <c-button onclick={ (ev) => update({ active: !state.active, max: true })}>Max</c-button>

        <c-dialog 
            title="Dialog" 
            message="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
            active={ state.active }
            oncancel={ cancel } 
            onconfirm={ confirm }
            max={ state.max }
        />
    </div>
    <script>
        import cButton from "../components/c-button.riot"
        import cDialog from "../components/c-dialog.riot"

        export default {
            components: {
                cDialog,
                cButton
            },
            state: {
                active: false,
                left: false,
                max: false
            },
            cancel() {
                console.log("dialog cancel")
                this.update({ active: false })
            },
            confirm() {
                console.log("dialog confirm")
                this.update({ active: false })
            }
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

Source Code: https://github.com/steevepay/riot-beercss/blob/main/examples/index.dialog.riot

Code details:

  1. Components are imported with import cDialog from "./components/c-dialog.riot"; then loaded in the components:{} Riot object.
  2. The dialog component is instantiated with <c-dialog/> on the HTML.
  3. The state of the Dialog is stored in the state:{} Riot object under the state.active Boolean property. The property is passed as an attribute, such as: <c-dialog active={ state.active }/>. When the button clicks, it sets the active property to true and displays the modal.
  4. The dialog's title and message are passed as attributes.
  5. If the cancel or confirm button is clicked, it will emit two custom events:
    • oncancel: it will execute the this.cancel() function, and close the modal.
    • onconfirm: it will execute the this.confirm() function, and close the modal.
  6. Both calls to action can be bound to a custom function: make an API call, add an HTML element, send an email, confirm a deletion and more!

Dialog Component Testing

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

Conclusion

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

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

Have a great day! Cheers 🍻

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