Dialog Component with RiotJS (Material Design)

Dialog Component with RiotJS (Material Design)

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:

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.

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>

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

Let’s break down the code:

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.
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.

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.
The modal is displayed only if the props.active exists and is positive; then, the class active is applied.
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>

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

Code details:

Components are imported with import cDialog from “./components/c-dialog.riot”; then loaded in the components:{} Riot object.
The dialog component is instantiated with <c-dialog/> on the HTML.
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.
The dialog’s title and message are passed as attributes.
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.

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:

Test with Vitest and Riot-SSR in a Node Environment
Test with Vitest in a JsDom Environment

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 🍻

Leave a Reply

Your email address will not be published. Required fields are marked *