<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oleksandr Tkachenko</title>
    <description>The latest articles on DEV Community by Oleksandr Tkachenko (@wolfriend).</description>
    <link>https://dev.to/wolfriend</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F983989%2Fe3e25236-879e-469b-8155-2a1d45349101.jpeg</url>
      <title>DEV Community: Oleksandr Tkachenko</title>
      <link>https://dev.to/wolfriend</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wolfriend"/>
    <language>en</language>
    <item>
      <title>Skeleton Mammoth - or how I've been solving the problem of reusable Skeleton Loaders.</title>
      <dc:creator>Oleksandr Tkachenko</dc:creator>
      <pubDate>Tue, 25 Jul 2023 16:50:47 +0000</pubDate>
      <link>https://dev.to/wolfriend/skeleton-mammoth-or-how-ive-been-solving-the-problem-of-reusable-skeleton-loaders-1b8b</link>
      <guid>https://dev.to/wolfriend/skeleton-mammoth-or-how-ive-been-solving-the-problem-of-reusable-skeleton-loaders-1b8b</guid>
      <description>&lt;h2&gt;
  
  
  Introduction.
&lt;/h2&gt;

&lt;p&gt;There are a lot of great articles on the Internet devoted to the skeleton loaders, that cover about their types, cases, and needs for their use. I will not list them here, you can easily find them in your favorite search engine.&lt;/p&gt;

&lt;p&gt;After investigating this topic in detail, I've decided to create by myself a very simple, flexible, reusable, customizable and lightweight solution that would suit most use cases.&lt;br&gt;
In this article, I will describe the process of creating this solution and turning it into a library, as well as the difficulties that I encountered while working on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Skeleton Loaders are?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can skip this section if you know what skeleton loaders are.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A skeleton loader, also known as a skeleton screen or content placeholder, is a user interface design pattern used to enhance the user experience during content loading in web and mobile applications. When data is being fetched or processed in the background, instead of displaying a blank or empty screen, a skeleton loader mimics page layout by providing users with a visual cue of what to expect, reducing perceived loading times and mitigating potential frustration.&lt;/p&gt;

&lt;p&gt;Here are examples of skeleton loaders from LinkedIn and Youtube:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuahe90ka1mkx9aepmea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuahe90ka1mkx9aepmea.png" alt="LinkedIn skeleton screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfqu7urnckhqqzod04u7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfqu7urnckhqqzod04u7.png" alt="YouTube skeleton screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why you have to use Skeleton Loaders?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Improve user experience: Skeleton loaders enhance the user experience by providing visual feedback and reducing the perception of content loading delays.&lt;/li&gt;
&lt;li&gt;Reduce bounce rate: Skeleton loaders can prevent users from leaving the page due to loading delays.&lt;/li&gt;
&lt;li&gt;Smooth transitions: They create smoother transitions between different states of a page or application.&lt;/li&gt;
&lt;li&gt;Unlike spinners, skeleton loaders attract the user's attention to progress rather than waiting time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Problems of most existing skeletons.
&lt;/h2&gt;

&lt;p&gt;Taking into account that there are a lot of examples of creating your own skeleton loaders or libraries that did it for you, there are still a number of problems with them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited customization: Many existing skeletons have limited customization options. It leads to mismatch of the actual content design and skeleton style.&lt;/li&gt;
&lt;li&gt;Although their purpose is to provide a visual representation, most of them are not adapted to users with visual impairments or those who using screen readers.&lt;/li&gt;
&lt;li&gt;Versatility and reusability. Most approaches to creating skeletons offer either creating a shallow copy of components placeholders, resulting in many similar copies, or essentially changing the structure of existing components. Both of the approaches require a lot of additional code and assets.&lt;/li&gt;
&lt;li&gt;Maintenance complexity: As websites evolve and content changes, keeping skeleton loaders up-to-date can become a maintenance burden.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Skeleton loaders alternatives.
&lt;/h2&gt;

&lt;p&gt;There are several "alternatives" to using skeletons. Looking ahead and answering whether there really are alternatives, my answer is no rather than yes. If we talk about correct usage, then the skeleton is one of the best solutions. Below, I will still give a couple of alternatives, along with their pros and cons.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;u&gt;Spinner&lt;/u&gt;.
&lt;/h3&gt;

&lt;p&gt;Spinners, are a common alternative to skeleton loaders. They consist of animated icons that rotate continuously, providing a visual cue that the content is loading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplicity: Simple implementation that, often requiring only a few lines of code or using pre-designed libraries.&lt;/li&gt;
&lt;li&gt;Universal understanding: Spinners are widely recognized across different platforms and applications, ensuring users understand that content is loading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited information: spinners do not provide any context about the content being loaded.&lt;/li&gt;
&lt;li&gt;Overlaps the entire page or most of it, not individual elements. What gives the feeling of loading not individual elements, but the entire site as a whole.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spinners are an integral part of interfaces, but they're not exactly suitable for replacing skeletons.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;u&gt;Progress Bar&lt;/u&gt;.
&lt;/h3&gt;

&lt;p&gt;A progress bar is a visual element that indicates the completion status of a task or process. It provides a linear representation, typically with a filled portion that grows gradually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precise feedback: Provides accurate and precise feedback on the completion status of a task.&lt;/li&gt;
&lt;li&gt;Time estimation: Progress bars can give users an estimate of the remaining time required for completion.&lt;/li&gt;
&lt;li&gt;Multi-Purpose: Progress bars can be used in various contexts and scenarios, making them a versatile component in web and application development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lack of context: In some cases, progress bars might not provide sufficient context about the actual task or process they represent.&lt;/li&gt;
&lt;li&gt;Implementation complexity: Creating progress bars with accurate representation and smooth animations can be complex, especially when dealing with varying task durations and responsiveness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Progress bars are more suitable for scenarios showing the progress of a file upload, or quantitative progress. They are often used at the top of pages to show the progress of an entire page loading. But they cannot serve as an equivalent replacement for the skeleton, because they are intended for other purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;u&gt;The absence of any visual.&lt;/u&gt;
&lt;/h3&gt;

&lt;p&gt;Yes, not having any loaders or placeholders is also an alternative. And in some cases, this will be a better solution than using unsuitable elements.&lt;br&gt;
The main and probably the only pros is that you don't need an additional time and resources spent on implementations. But here comes the obvious cons – a less attractive design for your site, and the perception of slower loading time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating versatile and reusable skeleton loader.
&lt;/h2&gt;

&lt;p&gt;After I had gained enough knowledge of what a skeletons are, when to use it, what they are, and approaches to their development, I tried to determine for myself what my final result should be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Versatile and Reusable.&lt;/strong&gt;&lt;br&gt;
There are a lot of examples over the internet, with overcomplicated approaches, where you have to create a separate skeleton for every component you want to have them on. In my case, I wanted it to be something singular that can be reused for most cases and not stick to any JavaScript framework (like React.js or Vue.js).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration Flexibility.&lt;/strong&gt;&lt;br&gt;
Since every project and every case can be very different, my skeleton needed to be able to be configurable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature rich.&lt;/strong&gt;&lt;br&gt;
In addition to the standard set of features, I wanted to fill it with support for additional useful and necessary features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lightweight and Dependencies-Free.&lt;/strong&gt;&lt;br&gt;
Lightweight and as free as possible from other 3rd party dependencies.&lt;/p&gt;

&lt;p&gt;All of these expectations and investigations, had led me to the fact, that my future skeleton had to be written in pure CSS, without any JavaScript, and third party dependencies. This makes it possible to be lightweight, and dependencies free. The main idea is that it inherits layouts of components it applied to, and customize them with their own styles.&lt;br&gt;
Perhaps in the future, for development purposes, it would make more sense to rewrite all this to the SCSS syntax, since this will make the code shorter and more reusable, and the final assembly will still compile to pure CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Base Card.
&lt;/h3&gt;

&lt;p&gt;As a basis example and for demo purposes, I'll use React.js and take some base card markup, to show how it works. But I remind you, that it's not tied to any of the frameworks, and at the end of the article there are will be links for source code of the library and demo.&lt;/p&gt;

&lt;p&gt;Here is a card markup example, that has it its own styles and doesn't know about the existence of skeleton yet.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__img-wrapper'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__img'&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`../../images/cards/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imgUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__body'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__details'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__title'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__subtitle'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;subtitle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In order for the skeleton to become active, it is only necessary to apply the parent class &lt;code&gt;sm-loading&lt;/code&gt; to the card itself and the child classes &lt;code&gt;sm-item-primary&lt;/code&gt; or &lt;code&gt;sm-item-secondary&lt;/code&gt; to those elements on which we want to see the skeleton. So the updated result will look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`card &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sm-loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__img-wrapper sm-item-primary'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__img'&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`../../images/cards/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imgUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__body'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__details'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__title sm-item-secondary'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'card__subtitle sm-item-secondary'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;subtitle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let me break it down and explain a few moments:&lt;br&gt;
In the following line of code&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`card &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sm-loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I apply the &lt;code&gt;sm-loading&lt;/code&gt; class depending on the condition. If the status of the &lt;code&gt;dataState.dataStatus&lt;/code&gt; is &lt;code&gt;loading&lt;/code&gt; then class will be applied, otherwise - no. The &lt;code&gt;sm-loading&lt;/code&gt; class should only be set/present while your data is loading. It's kind of a switcher. Only when it is present, child elements with the presence of appropriate classes &lt;code&gt;sm-item-primary&lt;/code&gt; or &lt;code&gt;sm-item-secondary&lt;/code&gt; will display the skeleton. So, only 3 classes that will make this skeleton work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Base skeleton styles.
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Root variables.
&lt;/h4&gt;

&lt;p&gt;In order to have a neat and reusable code, as well as the possibility of further configuration (overriding), I've created root variables with basic styles.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="c"&gt;/* Root variables.
--------------------------------------------------------------------------------*/&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* Light theme colors. */&lt;/span&gt;
    &lt;span class="py"&gt;--sm-color-light-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;204&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;204&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;204&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--sm-color-light-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;227&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;227&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;227&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--sm-color-light-animation-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;srgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="m"&gt;#fff&lt;/span&gt; &lt;span class="m"&gt;15%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-light-primary&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--sm-color-light-animation-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;srgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="m"&gt;#fff&lt;/span&gt; &lt;span class="m"&gt;15%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-light-secondary&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;/* Dark theme colors. */&lt;/span&gt;
    &lt;span class="py"&gt;--sm-color-dark-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--sm-color-dark-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--sm-color-dark-animation-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;srgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="m"&gt;#fff&lt;/span&gt; &lt;span class="m"&gt;2%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-dark-primary&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--sm-color-dark-animation-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;srgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="m"&gt;#fff&lt;/span&gt; &lt;span class="m"&gt;2%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-dark-secondary&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;/* Animations. */&lt;/span&gt;
    &lt;span class="py"&gt;--sm-animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--sm-animation-timing-function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--sm-animation-iteration-count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here the color values for the static (with no animation) and animated skeleton are set, as well as the animation settings.&lt;/p&gt;

&lt;h4&gt;
  
  
  Base styles.
&lt;/h4&gt;

&lt;p&gt;The next section of the file is dedicated to the base styles, that doesn't relate to any color scheme or configuration.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="c"&gt;/* Base styles.
Applied by default and not related to any of the color scheme.
--------------------------------------------------------------------------------*/&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;user-select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt;&lt;span class="nd"&gt;:before&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt;&lt;span class="nd"&gt;:before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Loading, please wait."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;inset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt;&lt;span class="nd"&gt;::placeholder&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt;&lt;span class="nd"&gt;::placeholder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt; &lt;span class="o"&gt;*,&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt; &lt;span class="nd"&gt;:empty:after&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt;&lt;span class="nd"&gt;:empty:after&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt; &lt;span class="nd"&gt;:empty:after&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt;&lt;span class="nd"&gt;:empty:after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"\00a0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Animations related styles. */&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;--sm--animation-wave&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;background-position-x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-200%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;--sm--animation-wave-reverse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;background-position-x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;--sm--animation-pulse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-animation-duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;--sm--animation-wave&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-animation-timing-function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-animation-iteration-count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As stated earlier, parent class &lt;code&gt;sm-loading&lt;/code&gt; is used to activate the skeleton loader style. The &lt;code&gt;sm-item-primary&lt;/code&gt; and &lt;code&gt;sm-item-secondary&lt;/code&gt; classes overrides element’s styles and displays a skeleton.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhjo7g1qfk3n6qvghh5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhjo7g1qfk3n6qvghh5z.png" alt="Skeleton Mammoth items structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this way, the layout styles and dimensions of elements (card component in our case) are persisted and inherited by the skeleton loader. Additionally, I would like to say that with this approach, we guarantee that all child elements of &lt;code&gt;sm-item-primary&lt;/code&gt; or &lt;code&gt;sm-item-secondary&lt;/code&gt; classes are hidden and at least have a &lt;a href="https://en.wikipedia.org/wiki/Non-breaking_space" rel="noopener noreferrer"&gt;Non-breaking space&lt;/a&gt; character. If an element contains no content at all, this symbol ensures that the element is displayed and rendered. There is also a part that is responsible for users of screen readers, and lets them know that the content is in the process of loading.  &lt;/p&gt;

&lt;p&gt;Further, there is a division into thematic sections, such as color scheme, animations, accessibility. Let's look at the color styles for the light theme.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="c"&gt;/* Light theme.
The library's default color scheme.
Styles applied to the light color scheme.
--------------------------------------------------------------------------------*/&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-light-primary&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-light-secondary&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Animations related styles. */&lt;/span&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="m"&gt;90deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-light-animation-primary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-light-primary&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="m"&gt;90deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-light-animation-secondary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sm-color-light-secondary&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Color scheme.
&lt;/h4&gt;

&lt;p&gt;With a CSS media feature &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme" rel="noopener noreferrer"&gt;prefers-color-scheme&lt;/a&gt; I've implemented automatic support of the light and dark themes. Depending on users' settings, it will be applied automatically. Of course, it's possible to set it manually, I'll talk about it later in the article.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="c"&gt;/* Dark theme.
Styles to apply if a user's device settings are set to use dark color scheme.
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
--------------------------------------------------------------------------------*/&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/*Omitted pieces of code.*/&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Accessibility.
&lt;/h4&gt;
&lt;h5&gt;
  
  
  Animations.
&lt;/h5&gt;

&lt;p&gt;By default, in the skeleton, I decided to make animation enabled, but there are cases when developers or users would prefer not to have it. And if for the former this may be dictated by design and requirements, then for the latter it may be due to vestibular motion disorders.&lt;br&gt;
For this, the CSS media feature &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion" rel="noopener noreferrer"&gt;prefers-reduced-motion&lt;/a&gt; comes to the rescue.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="c"&gt;/* Accessibility.
Disable animations if a user's device settings are set to reduced motion.
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
--------------------------------------------------------------------------------*/&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/*Omitted pieces of code.*/&lt;/span&gt;

    &lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;.sm-loading&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;/*Omitted pieces of code.*/&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Configuration.
&lt;/h3&gt;

&lt;p&gt;At this stage, the main styles are over, and the skeleton can be considered ready. But, I was haunted by the thought that I should be able to configure all of the above. What if I want to turn off the animation if I want to always have a dark theme?&lt;br&gt;
Since it is not possible for CSS to receive any values as arguments, like JavaScript functions do, the addition of JavaScript was excluded (at least at this stage). Because it would completely break the main concept of being as simple and lightweight as possible.&lt;br&gt;
But still, we can implement something similar to arguments if we know their values in advance. And here &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*" rel="noopener noreferrer"&gt;data-*&lt;/a&gt; attributes come to our aid. With their help, we can check for the presence of the value we need in the attribute, and apply the desired styles.&lt;br&gt;
I will show you how I've implemented it on a small piece of code, and you can find the full implementation in the source code at the link at the end of the article.  &lt;/p&gt;

&lt;p&gt;For example, if you want to explicitly use a dark theme, you need to make a JSON object:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;
data-* attributes can only work with strings, so it's significant to apply &lt;code&gt;JSON.stringify()&lt;/code&gt; method to the configuration object.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, pass this object to the custom attribute &lt;code&gt;data-sm-config&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card sm-loading"&lt;/span&gt; &lt;span class="na"&gt;data-sm-config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;Omitted&lt;/span&gt; &lt;span class="na"&gt;pieces&lt;/span&gt; &lt;span class="na"&gt;of&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is how it looks in the CSS file. If there is a value &lt;code&gt;"theme":"dark"&lt;/code&gt; in the &lt;code&gt;data-sm-config&lt;/code&gt;, apply desired styles.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="nc"&gt;.sm-loading&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-sm-config&lt;/span&gt;&lt;span class="o"&gt;&lt;em&gt;=&lt;/em&gt;&lt;/span&gt;&lt;span class="s2"&gt;'"theme":"dark"'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-primary&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;br&gt;
&lt;span class="nc"&gt;.sm-loading&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-sm-config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;'"theme":"dark"'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.sm-item-secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="c"&gt;/* Omitted pieces of code. */&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Advanced usage.&lt;br&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Overriding styles with global variables.
&lt;/h4&gt;

&lt;p&gt;Each project and case are unique, and it is impossible to predict and make everything versatile. Especially when it comes to colors. That is why, as was said at the beginning of the article, most of the values are placed in variables. If you want to adjust the default styles, just override appropriate variables in your own &lt;code&gt;*.css&lt;/code&gt; file inside the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:root" rel="noopener noreferrer"&gt;:root&lt;/a&gt; CSS pseudo-class.&lt;br&gt;
So, for example, if you want to change the color of the primary item (with a class &lt;code&gt;sm-item-primary&lt;/code&gt;), you only need to overwrite the corresponding variable:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c"&gt;/* Your own custom.css file: */&lt;/span&gt;&lt;br&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="py"&gt;--sm-color-light-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Live demo.&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F451asdweo4j1ktg3wu65.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F451asdweo4j1ktg3wu65.gif" alt="Skeleton Mammoth live demo animation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try out the finished result in action at the following link: &lt;a href="https://skeleton-mammoth-demo.onrender.com/" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's wrap it up.
&lt;/h2&gt;

&lt;p&gt;After I had studied the topic of skeleton loaders for a long time, their varieties, usage, approaches to develop, I managed to collect the essence of useful information and turn it into a final product. Having collected best practices, improved them and combined into a single entity, I've created the library called Skeleton Mammoth. I believe that I managed to achieve my goals and create a pretty good library with all the advantages described in this article. I hope that this library is able to benefit people when using it, or provide new knowledge and experience to create something of their own.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Show your support.
&lt;/h2&gt;

&lt;p&gt;If you find my library useful and would like to show your support, there are simple ways to do so:&lt;br&gt;
&lt;strong&gt;&lt;u&gt;Star the GitHub Repository&lt;/u&gt;:&lt;/strong&gt; This helps to increase its visibility and lets others know that the library has a strong user base.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Spread the Word&lt;/u&gt;:&lt;/strong&gt; You can introduce new users to the library by sharing information about it on any platform. Such as writing about it on a blog post, mentioning it on social media, or discussing it in relevant developer communities, it would be immensely helpful.  &lt;/p&gt;

&lt;p&gt;Below, I will post a list of useful links, including a link to the library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/WOLFRIEND/skeleton-mammoth" rel="noopener noreferrer"&gt;Skeleton Mammoth&lt;/a&gt; - GitHub repository of the library.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/skeleton-mammoth" rel="noopener noreferrer"&gt;NPM package&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://skeleton-mammoth-demo.onrender.com/" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/WOLFRIEND/skeleton-mammoth-demo" rel="noopener noreferrer"&gt;Live demo source code&lt;/a&gt; - Live demo GitHub repository.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>css</category>
      <category>skeleton</category>
      <category>ui</category>
    </item>
    <item>
      <title>Developing and testing sortable Drag and Drop components. Part 2 - Testing.</title>
      <dc:creator>Oleksandr Tkachenko</dc:creator>
      <pubDate>Fri, 09 Dec 2022 14:57:21 +0000</pubDate>
      <link>https://dev.to/wolfriend/developing-and-testing-sortable-drag-and-drop-components-part-2-testing-13lj</link>
      <guid>https://dev.to/wolfriend/developing-and-testing-sortable-drag-and-drop-components-part-2-testing-13lj</guid>
      <description>&lt;h2&gt;
  
  
  Foreword.
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/wolfriend/developing-and-testing-sortable-drag-and-drop-components-part-1-development-1bd4"&gt;previous article&lt;/a&gt;, you learned how to develop reorderable drag-and-drop components, now it's time to test them. In this part, you'll cover the application with unit tests with the BDD approach. I will not try to prove the usefulness or uselessness of unit tests in your projects, the final decision always depends on you, I'll just learn you how to do it. If you are not familiar with unit testing, take your time, to find and read a few articles to have a solid knowledge of what it is and how it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technologies and libraries used:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://testing-library.com" rel="noopener noreferrer"&gt;React Testing Library&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cucumber.io/" rel="noopener noreferrer"&gt;Cucumber&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/bencompton/jest-cucumber" rel="noopener noreferrer"&gt;Jest Cucumber&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cucumber and BDD approach.
&lt;/h2&gt;

&lt;p&gt;If you are already familiar with the Cucumber and BDD approach, you can safely skip this section and go to the next one.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is BDD?
&lt;/h3&gt;

&lt;p&gt;In simple words, this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Behavior-driven development (BDD) it's a methodology in testing practice that allows to describe how the application should behave in a very simple and human-readable language, that will be understood by all the team members. The idea is to create a specification, that will document your application in the manner the behavior a user expects to experience when interacting with it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is how Cucumber describes &lt;a href="https://cucumber.io/docs/bdd/" rel="noopener noreferrer"&gt;what is BDD&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;BDD is a way for software teams to work that closes the gap between business people and technical people by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encouraging collaboration across roles to build shared understanding of the problem to be solved&lt;/li&gt;
&lt;li&gt;Working in rapid, small iterations to increase feedback and the flow of value&lt;/li&gt;
&lt;li&gt;Producing system documentation that is automatically checked against the system’s behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We do this by focusing collaborative work around concrete, real-world examples that illustrate how we want the system to behave. We use those examples to guide us from concept through to implementation, in a process of continuous collaboration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a complete understanding, feel free to read the Cucumber documentation explanation or googling a few articles explaining this topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Cucumber?
&lt;/h3&gt;

&lt;p&gt;According to the &lt;a href="https://cucumber.io/docs/guides/overview/" rel="noopener noreferrer"&gt;Cucumber documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cucumber reads executable specifications written in plain text and validates that the software does what those specifications say. The specifications consists of multiple examples, or scenarios. For example:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
Scenario: Breaker guesses a word&lt;br&gt;
  Given the Maker has chosen a word&lt;br&gt;
  When the Breaker makes a guess&lt;br&gt;
  Then the Maker is asked to score&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
Each scenario is a list of steps for Cucumber to work through. Cucumber verifies that the software conforms with the specification and generates a report indicating ✅ success or ❌ failure for each scenario.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I guess, for now, you have a shallow picture of how it works, and you can proceed further, but anyway, I'll explain the process step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency installation and configuration.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dependencies installation.
&lt;/h3&gt;

&lt;p&gt;There are many ways, articles, and best practices on how to install and configure unit tests, and explaining it, will require a separate (or maybe more than one) article. And my goal here is to acquire basic knowledge of unit testing with Cucumber and cover the main functionality of the application that you developed in the last part. Therefore, all dependencies and configurations will be as simple as possible and minimally necessary for writing tests.&lt;br&gt;
Here is the list of required dependencies and dev-dependencies, which you also can find in the &lt;a href="https://github.com/WOLFRIEND/multiple-drag-n-drop/blob/master/package.json" rel="noopener noreferrer"&gt;package.json&lt;/a&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;devDependencies.&lt;/strong&gt; &lt;br&gt;
&lt;code&gt;File: package.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
"@testing-library/jest-dom"
"@testing-library/react"
"@testing-library/user-event"
"@types/jest"
"cross-env"
"jest-circus"
"jest-cucumber"
"jest-watch-typeahead"
"react-test-renderer"
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of them will be installed automatically with the &lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt;, and the rest you’ll install yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration.
&lt;/h3&gt;

&lt;p&gt;The easiest and quickest way to configure Jest is to specify the &lt;code&gt;jest&lt;/code&gt; key in the &lt;code&gt;package.json&lt;/code&gt; file. We only need one configuration, which concerns the &lt;a href="https://react-dnd.github.io/react-dnd/about" rel="noopener noreferrer"&gt;React DnD&lt;/a&gt; library, it’s &lt;a href="https://jestjs.io/docs/configuration#transformignorepatterns-arraystring" rel="noopener noreferrer"&gt;transformIgnorePatterns&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: package.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
"jest": {
 "transformIgnorePatterns": [
   "node_modules/(?!react-dnd)/"
 ]
}
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, React already has the command to run tests, but you need to modify it a little bit, so it will look the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
"test": "cross-env RTL_SKIP_AUTO_CLEANUP=true react-scripts test",
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the &lt;a href="https://github.com/kentcdodds/cross-env" rel="noopener noreferrer"&gt;cross-env&lt;/a&gt; library, you'll tell the &lt;code&gt;React Testing Library&lt;/code&gt; to skip auto cleanup after each test. More info and ways to configure here: &lt;a href="https://testing-library.com/docs/react-testing-library/setup/#skipping-auto-cleanup" rel="noopener noreferrer"&gt;Skipping Auto Cleanup&lt;/a&gt;.&lt;br&gt;
Now your configuration is enough to start writing tests, let's get started.&lt;/p&gt;
&lt;h2&gt;
  
  
  Writing Feature File - Specification.
&lt;/h2&gt;

&lt;p&gt;In order for Cucumber to understand the scenarios, they must follow some basic syntax rules, called &lt;a href="https://cucumber.io/docs/gherkin/" rel="noopener noreferrer"&gt;Gherkin&lt;/a&gt;. Gherkin documents are stored in &lt;code&gt;*.feature&lt;/code&gt; text files and are typically versioned in source control alongside the software. For more information, you can refer to the &lt;a href="https://cucumber.io/docs/guides/overview/#what-is-gherkin" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, but the easiest way is to see this with a real example.&lt;br&gt;
Create the &lt;code&gt;Container.feature&lt;/code&gt; file alongside the &lt;code&gt;Container.tsx&lt;/code&gt; file. This file consists of sections beginning with &lt;a href="https://cucumber.io/docs/gherkin/reference/?sbsearch=keyword#keywords" rel="noopener noreferrer"&gt;keywords&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://cucumber.io/docs/gherkin/reference/?sbsearch=keyword#feature" rel="noopener noreferrer"&gt;Feature&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The purpose of the Feature keyword is to provide a high-level description of a software feature, and to group related scenarios. The first primary keyword in a Gherkin document must always be Feature, followed by a : and a short text that describes the feature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In your case, you’ll test drag-and-drop functionality, so no need to complicate and let’s call it as it is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
Feature: Drag and Drop
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://cucumber.io/docs/gherkin/reference/?sbsearch=keyword#rule" rel="noopener noreferrer"&gt;Rule&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The purpose of the Rule keyword is to represent one business rule that should be implemented. It provides additional information for a feature. A Rule is used to group together several scenarios that belong to this business rule. A Rule should contain one or more scenarios that illustrate the particular rule.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Although this word is optional, let's include it for general development.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
Rule: Check drag and drop behavior
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://cucumber.io/docs/gherkin/reference/#examples" rel="noopener noreferrer"&gt;Scenario (Example)&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a concrete example that illustrates a business rule. It consists of a list of steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
Scenario: DropBoxContainer appearance
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://cucumber.io/docs/gherkin/reference/?sbsearch=keyword#steps" rel="noopener noreferrer"&gt;Steps&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each step starts with Given, When, Then, And, or But.&lt;br&gt;
Cucumber executes each step in a scenario one at a time, in the sequence you’ve written them in. When Cucumber tries to execute a step, it looks for a matching step definition to execute.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The steps for the first test are pretty easy, and straightforward.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
When I load the page
Then I should see the 'drop box container'
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eventually, the &lt;code&gt;Container.feature&lt;/code&gt; file will end up looking like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature: Drag and Drop

  Rule: Check drag and drop behavior

    Scenario: DropBoxContainer appearance
      When I load the page
      Then I should see the 'drop box container'

    Scenario Outline: DropBoxContainer data appearance
      When I load the page
      Then I should see the '&amp;lt;dishName&amp;gt;' dish

      Examples:
        | dishName          |
        | Stir-Fried Cat    |
        | Whisker Omelet    |
        | Cat tails         |
        | Delicate cat paws |
        | Wool pancakes     |
        | Cat's minion      |

    Scenario: Drag and drop behavior
      When I drag the 'Stir-Fried Cat' dish from the first table and drop it on the third table with the 'Cat tails' dish
      Then I should see that the first table contains 'Cat tails' dish
      And I should see that the third table contains 'Stir-Fried Cat' dish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data-testid.
&lt;/h2&gt;

&lt;p&gt;In order to be able to access your elements on the page, you need to find them in some way. For this purpose, you'll use the &lt;code&gt;data-testid&lt;/code&gt; attribute, by which elements can be found. It's like a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById" rel="noopener noreferrer"&gt;Document.getElementById()&lt;/a&gt;, but react testing library has its own methods for it. Mostly you'll use &lt;code&gt;ByTestId&lt;/code&gt;, but there are a lot more: &lt;a href="https://testing-library.com/docs/react-testing-library/cheatsheet/#queries" rel="noopener noreferrer"&gt;Cheat sheet&lt;/a&gt;.&lt;br&gt;
In order for the element to be found by the test id, you need to assign this attribute to it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/DnD/DropBox/DropBoxContainer.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
return (
 &amp;lt;div className={styles.container} data-testid={"drop box container"}&amp;gt;
   {selections.map((item, index) =&amp;gt; {
     return (
       &amp;lt;div className={styles.itemContainer} key={index}&amp;gt;
         &amp;lt;DropBox
           index={index}
           selection={selections[index]}
           updateSelectionsOrder={updateSelectionsOrder}
         /&amp;gt;
         &amp;lt;Table /&amp;gt;
       &amp;lt;/div&amp;gt;
     );
   })}
 &amp;lt;/div&amp;gt;
);
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this project, you also need to assign a few more of these attributes to other components:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/DnD/DropBox/DropBox.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
return (
 &amp;lt;div
   className={clsx(styles.dropContainer, {
     [styles.hovered]: isHovered,
   })}
   ref={drop}
   data-testid={`drop box ${index}`}
 &amp;gt;
   &amp;lt;DragBox dragItem={selection} index={index} /&amp;gt;
 &amp;lt;/div&amp;gt;
);
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;File: src/components/DnD/DragBox/DragBox.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
return (
 &amp;lt;div
   className={clsx(styles.container, {
     [styles.dragging]: isDragging,
   })}
   ref={drag}
   data-testid={name}
 &amp;gt;
   &amp;lt;img src={image} className={styles.icon} /&amp;gt;
   &amp;lt;p className={styles.name}&amp;gt;{name}&amp;lt;/p&amp;gt;
 &amp;lt;/div&amp;gt;
);
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test file.
&lt;/h2&gt;

&lt;p&gt;There are various approaches to organizing tests. As one of the approaches, I would advise separating them into separate files. One of the files will contain only the tests themselves, in this way you'll have an isolated logic and another file into which the tests will be imported will be responsible for the configuration.&lt;br&gt;
Let's look at the finished file, and I'll explain all the things that happen there.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.test.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";

import { loadFeature, defineFeature } from "jest-cucumber";
import { render, cleanup } from "@testing-library/react";

import { Container } from "./Container";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";

import {
  dragAndDropBehavior,
  dropBoxContainerAppearance,
  dropBoxContainerDataAppearance,
} from "./Container.definitions";

const feature = loadFeature("./Container.feature", {
  loadRelativePath: true,
});

defineFeature(feature, (test) =&amp;gt; {
  describe("Check drag and drop behavior", () =&amp;gt; {
    beforeAll(() =&amp;gt; {
      render(
        &amp;lt;DndProvider backend={HTML5Backend}&amp;gt;
          &amp;lt;Container /&amp;gt;
        &amp;lt;/DndProvider&amp;gt;
      );
    });
    afterAll(() =&amp;gt; {
      cleanup();
      jest.resetAllMocks();
    });
    dropBoxContainerAppearance(test);
    dropBoxContainerDataAppearance(test);
    dragAndDropBehavior(test);
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's talk about everything step by step.&lt;br&gt;
To connect the &lt;code&gt;Container.feature&lt;/code&gt; file with the test file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
import { loadFeature, defineFeature } from "jest-cucumber";
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For rendering tested components, and cleaning up traces after testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
import { render, cleanup } from "@testing-library/react";
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tested components and their dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
import { Container } from "./Container";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tests themselves. Their implementation will be explained later, for now just import them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
import {
  dragAndDropBehavior,
  dropBoxContainerAppearance,
  dropBoxContainerDataAppearance,
} from "./Container.definitions";
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use a feature file to be tested, use the &lt;code&gt;loadFeature&lt;/code&gt; function with the &lt;a href="https://github.com/bencompton/jest-cucumber/blob/master/docs/AdditionalConfiguration.md#relative-feature-file-paths" rel="noopener noreferrer"&gt;loadRelativePath&lt;/a&gt; param.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
const feature = loadFeature("./Container.feature", {
  loadRelativePath: true,
});
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, to add a jest test for each scenario in your feature file, wrap it with the &lt;a href="https://github.com/bencompton/jest-cucumber#add-a-jest-test-for-each-scenario-into-your-step-definition-file" rel="noopener noreferrer"&gt;defineFeature&lt;/a&gt;. You can separate each scenario from the feature file into a separate &lt;a href="https://jestjs.io/docs/api#describename-fn" rel="noopener noreferrer"&gt;describe&lt;/a&gt; block, but in this case, it’s not needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
defineFeature(feature, (test) =&amp;gt; {
  describe("Check drag and drop behavior", () =&amp;gt; {
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;a href="https://jestjs.io/docs/api#beforeallfn-timeout" rel="noopener noreferrer"&gt;beforeAll&lt;/a&gt; function, you'll render your component to be tested with the &lt;a href="https://testing-library.com/docs/react-testing-library/api/#render" rel="noopener noreferrer"&gt;render&lt;/a&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
    beforeAll(() =&amp;gt; {
      render(
        &amp;lt;DndProvider backend={HTML5Backend}&amp;gt;
          &amp;lt;Container /&amp;gt;
        &amp;lt;/DndProvider&amp;gt;
      );
    });
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To clean up everything that you "littered", use &lt;a href="https://testing-library.com/docs/react-testing-library/api/#cleanup" rel="noopener noreferrer"&gt;cleanup&lt;/a&gt; and &lt;a href="https://jestjs.io/docs/jest-object#jestresetallmocks" rel="noopener noreferrer"&gt;resetAllMocks&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
    afterAll(() =&amp;gt; {
      cleanup();
      jest.resetAllMocks();
    });
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, just specify the tests that should be run.&lt;br&gt;
You will start implementing them in the next chapter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
    dropBoxContainerAppearance(test);
    dropBoxContainerDataAppearance(test);
    dragAndDropBehavior(test);
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tests definitions.
&lt;/h2&gt;

&lt;p&gt;This is where you will write tests for each scenario from the feature file.&lt;br&gt;
Create a function with a descriptive name that will accept one argument - jest &lt;a href="https://jestjs.io/docs/jest-object#jestresetallmocks" rel="noopener noreferrer"&gt;test&lt;/a&gt;, and implement the test inside this function. The test function accepts two arguments: name - which should match the scenario from the feature file, and test implementation, which also could be moved to a separate function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.definitions.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const dropBoxContainerAppearance = (test): void =&amp;gt; {
  test("DropBoxContainer appearance", async ({ when, then }) =&amp;gt; {
    // Omitted pieces of code.
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test implementation is pretty simple, and should reflect the feature file steps. So, for the "&lt;code&gt;When I load the page&lt;/code&gt;" step, you describe it as it is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Omitted pieces of code.
    when("I load the page", async () =&amp;gt; {
      await waitFor(() =&amp;gt; "pending");
    });
 // Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since your component is rendered with the render function in the &lt;code&gt;src/components/Container/Container.test.tsx&lt;/code&gt; file, you don't need any additional implementation here. You just wait till the component will be rendered with the &lt;a href="https://testing-library.com/docs/dom-testing-library/api-async/#waitfor" rel="noopener noreferrer"&gt;waitFor&lt;/a&gt; function, and return some semantic description.&lt;br&gt;
Next, as mentioned in the "&lt;code&gt;Then I should see the 'drop box container'&lt;/code&gt;" step, you need to make sure that this element is present on the page. Please note that &lt;code&gt;'drop box container'&lt;/code&gt; has been replaced here with a wildcard, and is used as the &lt;code&gt;testId&lt;/code&gt; argument. You can have many of these wildcards and arguments, the main thing is that you need to be consistent with the feature file.&lt;br&gt;
To find the element by &lt;code&gt;testId&lt;/code&gt;, use &lt;a href="https://testing-library.com/docs/queries/bytestid" rel="noopener noreferrer"&gt;ByTestId&lt;/a&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
const dropBoxContainer = screen.getByTestId(testId);
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And make sure the given element is present on the page with the &lt;a href="https://jestjs.io/docs/29.0/expect#expectvalue" rel="noopener noreferrer"&gt;expect&lt;/a&gt; and &lt;a href="https://github.com/testing-library/jest-dom#tobeinthedocument" rel="noopener noreferrer"&gt;toBeInTheDocument&lt;/a&gt; functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
expect(dropBoxContainer).toBeInTheDocument();
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ultimately, your test for the first scenario will look the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const dropBoxContainerAppearance = (test): void =&amp;gt; {
  test("DropBoxContainer appearance", async ({ when, then }) =&amp;gt; {
    when("I load the page", async () =&amp;gt; {
      await waitFor(() =&amp;gt; "pending");
    });

    then(/^I should see the '(.*)'$/, async (testId) =&amp;gt; {
      await waitFor(() =&amp;gt; {
        const dropBoxContainer = screen.getByTestId(testId);
        expect(dropBoxContainer).toBeInTheDocument();
      });
    });
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'll run your first test with the &lt;code&gt;npm run test&lt;/code&gt; command from the project root folder (without forgetting that the feature file should contain only this scenario), you'll see that it passed successfully.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym87d5xs0we7xvtyy7gv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym87d5xs0we7xvtyy7gv.png" alt="Test run result" width="754" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next scenario is pretty similar to the previous one, except you need to use the &lt;a href="https://cucumber.io/docs/gherkin/reference/#scenario-outline" rel="noopener noreferrer"&gt;Scenario Outline&lt;/a&gt; here.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Scenario Outline keyword can be used to run the same Scenario multiple times, with different combinations of values.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this way, you'll test that all desired dishes are present on the tables.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Omitted pieces of code.
    Scenario Outline: DropBoxContainer data appearance
      When I load the page
      Then I should see the '&amp;lt;dishName&amp;gt;' dish

      Examples:
        | dishName          |
        | Stir-Fried Cat    |
        | Whisker Omelet    |
        | Cat tails         |
        | Delicate cat paws |
        | Wool pancakes     |
        | Cat's minion      |
 // Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation of the test is also very similar, but here you use the &lt;a href="https://testing-library.com/docs/queries/bytext" rel="noopener noreferrer"&gt;getByText&lt;/a&gt; search method.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const dropBoxContainerDataAppearance = (test): void =&amp;gt; {
  test("DropBoxContainer data appearance", async ({ when, then }) =&amp;gt; {
    when("I load the page", async () =&amp;gt; {
      await waitFor(() =&amp;gt; "pending");
    });

    then(/^I should see the '(.*)' dish$/, async (dishNameTestId) =&amp;gt; {
      await waitFor(() =&amp;gt; {
        const dishName = screen.getByText(dishNameTestId);
        expect(dishName).toBeInTheDocument();
      });
    });
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Drag and Drop testing.
&lt;/h2&gt;

&lt;p&gt;For testing drag-and-drop behavior (and not only), React Testing Library has a helper method, &lt;a href="https://testing-library.com/docs/dom-testing-library/api-events/#fireevent" rel="noopener noreferrer"&gt;fireEvents&lt;/a&gt;. As the name suggests, it's intended for firing DOM events, such as "click", "change", "drag", etc.&lt;br&gt;
The scenario for testing drag and drop assumes that if you take a dish from one table and put it on another table, they should "swap places". Just take a look and everything will become clear.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Omitted pieces of code.
    Scenario: Drag and drop behavior
      When I drag the 'Stir-Fried Cat' dish from the first table and drop it on the third table with the 'Cat tails' dish
      Then I should see that the first table contains 'Cat tails' dish
      And I should see that the third table contains 'Stir-Fried Cat' dish
 // Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first step, you need to perform a drag-and-drop of dishes. To do this, you need a draggable object and a target (drop area) where you want to drop this object.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.definitions.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
const firstDragBox = screen.getByTestId(firstDragBoxTestId);
const thirdDropBox = screen.getByTestId("drop box 2");
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drag and drop is done in several steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start drag of the draggable object (dish).&lt;/li&gt;
&lt;li&gt;Enter the draggable object into the droppable area (table).&lt;/li&gt;
&lt;li&gt;Drag over that droppable area.&lt;/li&gt;
&lt;li&gt;Drop the draggable object.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, the first step will look the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.definitions.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Omitted pieces of code.
when(
 /^I drag the '(.*)' dish from the first table and drop it on the third table with the '(.*)' dish$/,
 async (firstDragBoxTestId) =&amp;gt; {
   await waitFor(() =&amp;gt; {
     const firstDragBox = screen.getByTestId(firstDragBoxTestId);
     const thirdDropBox = screen.getByTestId("drop box 2");
     fireEvent.dragStart(firstDragBox);
     fireEvent.dragEnter(thirdDropBox);
     fireEvent.dragOver(thirdDropBox);
     fireEvent.drop(thirdDropBox);
   });
 }
);
 // Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Further, it remains only to make sure that the expected dishes are placed on their tables.&lt;br&gt;
To do this, you can use the help of yet another "within" method, with which you restrict the search area. That is, instead of searching the entire document.body, it will only search in the specified area. In your case, you are limiting your search to the drop boxes you want.&lt;br&gt;
Full implementation of the last scenario:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.definitions.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const dragAndDropBehavior = (test): void =&amp;gt; {
  test("Drag and drop behavior", async ({ when, then, and }) =&amp;gt; {
    when(
      /^I drag the '(.*)' dish from the first table and drop it on the third table with the '(.*)' dish$/,
      async (firstDragBoxTestId) =&amp;gt; {
        await waitFor(() =&amp;gt; {
          const firstDragBox = screen.getByTestId(firstDragBoxTestId);
          const thirdDropBox = screen.getByTestId("drop box 2");
          fireEvent.dragStart(firstDragBox);
          fireEvent.dragEnter(thirdDropBox);
          fireEvent.dragOver(thirdDropBox);
          fireEvent.drop(thirdDropBox);
        });
      }
    );
    then(
      /^I should see that the first table contains '(.*)' dish$/,
      async (dishNameTestId) =&amp;gt; {
        await waitFor(() =&amp;gt; {
          const firstDropBox = screen.getByTestId("drop box 0");
          expect(
            within(firstDropBox).getByText(dishNameTestId)
          ).toBeInTheDocument();
        });
      }
    );
    and(
      /^I should see that the third table contains '(.*)' dish$/,
      async (dishNameTestId) =&amp;gt; {
        await waitFor(() =&amp;gt; {
          const thirdDropBox = screen.getByTestId("drop box 2");
          expect(
            within(thirdDropBox).getByText(dishNameTestId)
          ).toBeInTheDocument();
        });
      }
    );
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, your tests are written and ready to run successfully.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8cc1zdqu4mt5nkw5eom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8cc1zdqu4mt5nkw5eom.png" alt="Test run result" width="800" height="989"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Debugging.
&lt;/h2&gt;

&lt;p&gt;There may be cases where something may not work, and some of the values will not be as expected. To do this, instead of the usual console.log(), which is not appropriate here, the screen.debug() method will come to the rescue. It takes three arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;element?: Array&amp;lt;Element | HTMLDocument&amp;gt; | Element | HTMLDocument,
maxLength?: number,
options?: OptionsReceived,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to display the entire element, just specify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen.debug(undefined, 99999);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this way, you don't stick to a specific element and show the entire document, and the second argument it's just a large number to not restrict rendered information.&lt;br&gt;
If you want to display only the area you want, it might look like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.definitions.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Omitted pieces of code.
const firstDropBox = screen.getByTestId("drop box 0");
screen.debug(firstDropBox, 99999);
// Omitted pieces of code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result will be:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feji7ovajui39edc8i08f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feji7ovajui39edc8i08f.png" alt="Test debug" width="544" height="892"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion.
&lt;/h2&gt;

&lt;p&gt;This was the final part of this article, in which you learned how to test draggable components. As in the previous article, only high-level testing possibilities were shown here. For a deeper understanding, check the links below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links.
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/WOLFRIEND/multiple-drag-n-drop" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://reorderable-drag-and-drop.onrender.com/" rel="noopener noreferrer"&gt;Online demo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react-dnd.github.io/react-dnd/about" rel="noopener noreferrer"&gt;React DnD library&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cucumber.io/" rel="noopener noreferrer"&gt;Cucumber&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;Testing Library&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/testing-library/jest-dom" rel="noopener noreferrer"&gt;Jest-dom&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>draganddrop</category>
      <category>programming</category>
    </item>
    <item>
      <title>Developing and testing sortable Drag and Drop components. Part 1 - Development.</title>
      <dc:creator>Oleksandr Tkachenko</dc:creator>
      <pubDate>Sun, 04 Dec 2022 10:31:19 +0000</pubDate>
      <link>https://dev.to/wolfriend/developing-and-testing-sortable-drag-and-drop-components-part-1-development-1bd4</link>
      <guid>https://dev.to/wolfriend/developing-and-testing-sortable-drag-and-drop-components-part-1-development-1bd4</guid>
      <description>&lt;h2&gt;
  
  
  Foreword.
&lt;/h2&gt;

&lt;p&gt;In this series of articles, we will consider the task of developing and testing sortable Drag-and-Drop components. There are a lot of scenarios for using drag-and-drop behavior, here are a few of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uploading or removing files and images (banalest).&lt;/li&gt;
&lt;li&gt;Sortable tables.&lt;/li&gt;
&lt;li&gt;Sortable notes or stickers.&lt;/li&gt;
&lt;li&gt;Reorderable tabs. Look at your browser's opened tabs, you can reorder them with the drag-and-drop behavior.&lt;/li&gt;
&lt;li&gt;Captchas validation (assembling the puzzle).&lt;/li&gt;
&lt;li&gt;Games (chess and checkers).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the first part of the article, we will create a small semblance of a restaurant in the style of the cult sitcom &lt;a href="https://en.wikipedia.org/wiki/ALF_(TV_series)" rel="noopener noreferrer"&gt;ALF&lt;/a&gt;, with the functionality of drag-and-drop sorting of dishes between visitors' tables. You can play with the working online demo by the following link &lt;a href="https://reorderable-drag-and-drop.onrender.com/" rel="noopener noreferrer"&gt;Demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's imagine that in our restaurant, a newbie junior waiter mixed up the orders of visitors and placed all dishes incorrectly, and our task is to put everything in its place. To do this, we need to drag the desired dish and drop it on the appropriate table. Let's help our waiter and put things in order here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technologies and libraries used:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;Typescript&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react-dnd.github.io/react-dnd" rel="noopener noreferrer"&gt;React DnD&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Project Structure.
&lt;/h2&gt;

&lt;p&gt;The structure is as simple and clear as possible, with abstract naming to not stick to some particular use case. All data used is mocked, so as not to be distracted by the unnecessary implementation. Styles are used to make things more visually pleasing, but they don't affect the project in any way, so don’t focus on them. Some examples will omit optional code parts with appropriate comments:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Omitted pieces of code.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Drag-and-Drop Provider.
&lt;/h2&gt;

&lt;p&gt;To enable the drag-and-drop functionality in our application, we have to wrap the desired component in the &lt;a href="https://react-dnd.github.io/react-dnd/docs/api/dnd-provider#dndprovider" rel="noopener noreferrer"&gt;DnD Provider&lt;/a&gt; along with the &lt;code&gt;backend&lt;/code&gt; prop.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/Container/Container.tsx&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { DropBoxContainer } from "../DnD/DropBox/DropBoxContainer";

// Omitted pieces of code.
&amp;lt;DndProvider backend={HTML5Backend}&amp;gt;
 &amp;lt;DropBoxContainer selectionsData={MOCK_DATA} /&amp;gt;
&amp;lt;/DndProvider&amp;gt;
// Omitted pieces of code.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Drop Box container.
&lt;/h2&gt;

&lt;p&gt;The next step is to create the &lt;code&gt;DropBoxContainer&lt;/code&gt;. It is responsible for keeping the data in the correct order, the sorting functionality, and displaying the &lt;code&gt;DropBox&lt;/code&gt; components. In a real case, data storage can be taken to a higher level, like &lt;a href="https://reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;React context&lt;/a&gt; or &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux store&lt;/a&gt;, as well as sorting functionality, which can be placed into separate utils files, but for the sake of the demo, it will be pretty enough.&lt;br&gt;
Inside this file, we will iterate over all of our data (visitor's food tables in our case) and render each item as a separate &lt;code&gt;DropBox&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: ​​src/components/DnD/DropBox/DropBoxContainer.tsx&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Omitted pieces of code.
&amp;lt;div className={styles.container}&amp;gt;
 {selections.map((item, index) =&amp;gt; {
   return (
     &amp;lt;div className={styles.itemContainer}&amp;gt;
       &amp;lt;DropBox
         key={index}
         index={index}
         selection={selections[index]}
         updateSelectionsOrder={updateSelectionsOrder}
       /&amp;gt;
       &amp;lt;Table /&amp;gt;
     &amp;lt;/div&amp;gt;
   );
 })}
&amp;lt;/div&amp;gt;
// Omitted pieces of code.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Drop Box item.
&lt;/h2&gt;

&lt;p&gt;Go ahead and create the &lt;code&gt;DropBox&lt;/code&gt; file that will act as a separate container/box for each draggable file. For more clear understanding, I will try to explain in simple words using the example of our application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;File: src/components/DnD/DropBox/DropBoxContainer.tsx&lt;/code&gt; - It’s like a food court or food hall, the place where all visitors sit at their tables. It’s some kind of container where all things happen.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;File: src/components/DnD/DropBox/DropBox.tsx&lt;/code&gt; - This is a specific table at which the visitor and their food are placed. It's a place where draggable items are dropped.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;File: src/components/DnD/DragBox/DragBox.tsx&lt;/code&gt; - This is the plate of the food itself, or in other words, it’s a draggable item.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;DropBox&lt;/code&gt; component accepts several props:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index&lt;/code&gt; - that we are taking from the map() function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;selection&lt;/code&gt; - it’s a data item, which in our case is a food dish.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;updateSelectionsOrder&lt;/code&gt; - function to update items order.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here comes the first "magic" associated with the drop behavior.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/DnD/DropBox/DropBox.tsx&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Omitted pieces of code.
const [isHovered, setIsHovered] = useState(false);

const [_, drop] = useDrop({
 accept: [DragTypes.Card],
 drop(item: DragItem) {
   updateSelectionsOrder(item.index, index);
 },
 collect: (monitor) =&amp;gt; {
   if (monitor.isOver()) {
     setIsHovered(true);
   } else {
     setIsHovered(false);
   }
 },
});
// Omitted pieces of code.



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As the documentation says about the &lt;a href="https://react-dnd.github.io/react-dnd/docs/api/use-drop#usedrop" rel="noopener noreferrer"&gt;useDrop&lt;/a&gt; hook:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The useDrophook provides a way for you to wire in your component into the DnD system as a drop target. By passing in a specification into the useDrophook, you can specify including what types of data items the drop-target will accept, what props to collect, and more. This function returns an array containing a ref to attach to the Drop Target node and the collected props.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s take a closer look piece by piece:&lt;br&gt;
&lt;code&gt;accept&lt;/code&gt;:  Required. A string, a symbol, or an array of either. Specifies a type to which the drop target will react, it will only react to the items produced by the drag sources only of the specified type or types. Simply put, if you'll try to drag an item over the drop target that doesn't have a type specified in the "accept" property, the drop target will not react to it and will ignore it. It prevents interaction with unwanted and unspecified items.&lt;br&gt;
&lt;code&gt;drop(item, monitor)&lt;/code&gt;: Optional. Called when a compatible drag item is dropped on the drop target. In our case, when we drag a dish and drop it on some table, this function is called, and we run the provided &lt;code&gt;updateSelectionsOrder&lt;/code&gt; callback, to update the arrangement of dishes.&lt;br&gt;
&lt;code&gt;collect&lt;/code&gt;: Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitor and props. We use it with the &lt;a href="https://react-dnd.github.io/react-dnd/docs/api/drop-target-monitor#droptargetmonitor" rel="noopener noreferrer"&gt;DropTargetMonitor&lt;/a&gt;, to receive information on whether the drag operation is in progress. It helps to apply some visual effects when we hover a dish over the table and draw a dashed border around items.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9c13wk5t7r5pvwz70g8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9c13wk5t7r5pvwz70g8.gif" alt="Image description"&gt;&lt;/a&gt;To designate a drop target, we're attaching the &lt;code&gt;drop&lt;/code&gt; returned value from the &lt;a href="https://react-dnd.github.io/react-dnd/docs/api/use-drop#usedrop" rel="noopener noreferrer"&gt;useDrop&lt;/a&gt; hook to the drop-target portion of the DOM.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/DnD/DropBox/DropBox.tsx&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Omitted pieces of code.
return (
 &amp;lt;div
   className={clsx(styles.dropContainer, {
     [styles.hovered]: isHovered,
   })}
   ref={drop}
 &amp;gt;
   &amp;lt;DragBox dragItem={selection} index={index} /&amp;gt;
 &amp;lt;/div&amp;gt;
);
// Omitted pieces of code.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Drag Box item.
&lt;/h2&gt;

&lt;p&gt;Finally, we reached our target and most delicious items (according to the ALF's taste). Let’s consider our &lt;code&gt;DragBox&lt;/code&gt; dish. It receives two props:&lt;br&gt;
&lt;code&gt;dragItem&lt;/code&gt;: Data of the draggable item. For this application, it was simplified to only "id", "name", and "icon" properties.&lt;br&gt;
&lt;code&gt;index&lt;/code&gt;: Index of the draggable item, that will be used to operate with the drag behavior that we will explore further.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/DnD/DragBox/DragBox.tsx&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Omitted pieces of code.
const [{ isDragging }, drag] = useDrag({
 type: DragTypes.Card,
 item: { type: DragTypes.Card, id, index, name, icon },
 collect: (monitor) =&amp;gt; ({
   isDragging: monitor.isDragging(),
 }),
});
// Omitted pieces of code.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To wire our component as a drag source, we use the &lt;a href="https://react-dnd.github.io/react-dnd/docs/api/use-drag#usedrag" rel="noopener noreferrer"&gt;useDrag&lt;/a&gt; hook. Here we specify the &lt;code&gt;type&lt;/code&gt; that we discussed above in the &lt;a href="https://react-dnd.github.io/react-dnd/docs/api/use-drop#usedrop" rel="noopener noreferrer"&gt;useDrop&lt;/a&gt; hook, &lt;code&gt;item&lt;/code&gt; - draggable item data that will be passed to the &lt;code&gt;drop&lt;/code&gt; function of the &lt;a href="https://react-dnd.github.io/react-dnd/docs/api/use-drop#usedrop" rel="noopener noreferrer"&gt;useDrop&lt;/a&gt; hook in the &lt;code&gt;DropBox&lt;/code&gt; component, and &lt;code&gt;collect&lt;/code&gt; function to receive info whether our item is dragging, for visual effects.&lt;br&gt;
To designate a drag item, we're attaching the &lt;code&gt;drag&lt;/code&gt; returned value from the &lt;a href="https://react-dnd.github.io/react-dnd/docs/api/use-drag" rel="noopener noreferrer"&gt;useDrag&lt;/a&gt; hook to the draggable portion of the DOM.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;File: src/components/DnD/DragBox/DragBox.tsx&lt;/code&gt;&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;// Omitted pieces of code.&lt;br&gt;
return (&lt;br&gt;
 &amp;lt;div&lt;br&gt;
   className={clsx(styles.container, {&lt;br&gt;
     [styles.dragging]: isDragging,&lt;br&gt;
   })}&lt;br&gt;
   ref={drag}&lt;br&gt;
 &amp;gt;&lt;br&gt;
   &amp;lt;img src={image} className={styles.icon} /&amp;gt;&lt;br&gt;
   &amp;lt;p className={styles.name}&amp;gt;{name}&amp;lt;/p&amp;gt;&lt;br&gt;
 &amp;lt;/div&amp;gt;&lt;br&gt;
);&lt;br&gt;
// Omitted pieces of code.&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion.&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Finally, we have a working version of the application with sortable draggable components, which, with the necessary adaptation, will surely find its use in the real world. In this example, we have considered only a small part of the capabilities of this library, which will be enough to start creating your own implementation. For a deeper understanding, check out the &lt;a href="https://react-dnd.github.io/react-dnd/docs/overview" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, which also has a great "&lt;a href="https://react-dnd.github.io/react-dnd/examples" rel="noopener noreferrer"&gt;Examples&lt;/a&gt;" section where you can find useful usage examples. In the next article, we will develop unit tests for these components and cover the main use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links.
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/WOLFRIEND/multiple-drag-n-drop" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://reorderable-drag-and-drop.onrender.com/" rel="noopener noreferrer"&gt;Online demo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react-dnd.github.io/react-dnd/about" rel="noopener noreferrer"&gt;React DnD library&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>draganddrop</category>
      <category>react</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
