JSFiddle - React, Tailwind, and code Playground

Copyright © 2018 by Isaac J. Lee, all rights reserved.

HTML

<script src="https://code.jquery.com/qunit/qunit-2.6.1.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.6.1.css">
<div id='myTest'>
    <div class='help-link'>
        <a href='https://api.qunitjs.com/' target='_blank' rel='noreferrer'>Link to QUnit API</a>
    </div>
    <div id='qunit'></div>
    <div id='qunit-fixture'></div>
</div>

<div id='myPage'>
    <div data-test-my-instructions id='myInstructions'>
        You can submit the form to add a table row.
    </div>

    <div id='myContainer'>
        <table data-test-my-table id='myTable'>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Description</th>
                </tr>
            </thead>

            <tbody id='myTableBody'>
                <!-- Will be dynamically generated -->
            </tbody>
        </table>

        <form data-test-my-form id='myForm'>
            <fieldset class='form-fieldset'>
                <legend>Primary Information</legend>

                <p data-test-my-form-item='name' class='form-item'>
                    <label for='name'>Name: <abbr title='required'>*</abbr></label>
                    <input id='name' type='text' required/>
                </p>

                <p data-test-my-form-item='description' class='form-item'>
                    <label for='description'>Description:</label>
                    <input id='description' type='text'/>
                </p>

                <input data-test-my-submit-button class='submit-button' type='submit' value='Submit'/>
            </fieldset>
        </form>
    </div>
</div>

SCSS

#myInstructions {
    margin-bottom: 24px;

    color: steelblue;
    font-family: 'Arial';
    font-size: 24px;
    font-weight: bold;
}

#myContainer {
    display: flex;

    #myTable {
        th, td {
            border-bottom: 1px solid #ddd;
            padding: 8px 12px;

            font-family: 'Arial';
        }

        th {
            background-color: #eee;

            &:hover {
                background-color: #ddd;
                cursor: pointer;
            }
        }
    }

    #myForm {
        width: 40%;
        margin-left: 20px;

        .form-fieldset {
            background-color: #fafafa;
            font-family: 'Arial';

            .form-item {
                label, input {
                    display: block;
                    margin-bottom: 8px;
                }

                label abbr {
                    border: none;
                    text-decoration: none;
                }

                input {
                    width: 100%;
                    height: 20px;
                }
            }

            .submit-button {
                float: right;
                margin-top: 8px;
                width: 50%;

                font-size: 16px;
            }
        }
    }
}

#myTest {
    margin-bottom: 20px;

    .help-link {
        font-family: 'Arial';
        font-size: 24px;
        margin-bottom: 16px;
    }
}

#myPage {
    border: 1px solid black;
    padding: 8px;
}

JavaScript

/**************************************************

    Get mock data

**************************************************/
let data = [];
let nextAvailableId = 0;

// Use this to reset what we get from the backend
const loadData = () => {
    data = [
        {
            id: 0,
            name: 'V',
            description: 'v is for victory'
        },
        {
            id: 1,
            name: 'e',
            description: undefined
        },
        {
            id: 2,
            name: 'o',
            description: undefined
        },
        {
            id: 3,
            name: 'h',
            description: 'h is for hello world, this is my first program'
        },
        {
            id: 4,
            name: 'U',
            description: 'u is for user experience'
        },
        {
            id: 5,
            name: 'y',
            description: 'y is for yo-yo'
        }
    ];

    nextAvailableId = data.length;
};

// Get the data for the first time
loadData();


/**************************************************

    Update the table

**************************************************/
let myTableBody = document.getElementById('myTableBody');

const displayTable = (data) => {
    myTableBody.innerHTML = '';

    data.forEach((datum, index) => {
        let row = myTableBody.insertRow();

        // Display name
        let cell_name = row.insertCell(0);
        let node_name = document.createTextNode(datum.name);

        cell_name.appendChild(node_name);

        // Display description (an optional field)
        let cell_description = row.insertCell(1);
        let node_description = document.createTextNode(datum.description || '');

        cell_description.appendChild(node_description);
        
        // Mark the row with a data-test tag
        row.setAttribute('data-test-my-row', '');
    });
};

// Display the table for the first time
displayTable(data);


/**************************************************

    Listen to...