Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save chrismarksus/bd8a3d9909e6ce78255d2e46658d1b38 to your computer and use it in GitHub Desktop.
Save chrismarksus/bd8a3d9909e6ce78255d2e46658d1b38 to your computer and use it in GitHub Desktop.
_Break HTML up into testable methods

_Break HTML up into testable methods

The original version that was used as a reference was the single large method with and if/then/else that tested a variable for one or zero. Then set a variable to a string for html with 3 radio buttons. The index number was used to seed id name and value passed to methods.

ISSUES:

  • 3 large chunks of HTML with repeatative radio buttons that only difference was a checked attribute
  • Difficult to debug the HTML
  • Difficult the edit or change the HTML
  • Side-effects in the method was mix with code that could be moved to a pure function
  • Javascript is mixed in with the HTML

A Pen by Chris Marks on CodePen.

License.

<div>
<table id="js-table-area"></table>
</div>
/*
The original version that was used as a reference was the single large method
with and if/then/else that tested a variable for one or zero. Then set a variable
to a string for html with 3 radio buttons. The index number was used to seed id
name and value passed to methods.
ISSUES:
- 3 large chunks of HTML with repeatative radio buttons that only difference was a
checked attribute
- Difficult to debug the HTML
- Difficult the edit or change the HTML
- Side-effects in the method was mix with code that could be moved to a pure function
- Jsavascript is mixed in with the HTML
*/
/* Pure Functions */
/**
* Build the HTML for a radio button for the selecting type
*
* index - number - The index of the data item on the page
* title - String - The text for the label
* num - number - The number (1,0,-1) passed to as the selection type
* checked - boolean - Is the radio button checked or not
*/
function setRadioBtn(index, title, num, checked=false){
/*
NOTE:
The onClick in the html: I would remove this and replace this
this with data-* attributes. then attach a click listener that
adds the onClick functionality to the element
EXAMPLE:
data-index="${index}"
data-number="${num}"
*/
let html = `
<div class="form-check form-check-inline">
<input
name="entry${index}"
class="form-check-input"
type="radio" id="${title.toLowerCase()}"
value="${title.toLowerCase()}"
onclick="button(${index}, ${num})"
${(checked)? "checked": ""}>
<label class="form-check-label">${title}</label>
</div>
`;
return html;
}
/**
* Build the radion btton for selection from a matrix of choices
*
* index - number - The index of the tweet
* matrix - array - Contains a title, number (1,0,-1), and a boolean for the checked state
*/
function setRadioBtns(index, matrix){
let btns = "";
for(let m in matrix){
btns += `${setRadioBtn(index, ...matrix[m])}`
}
let html = `
<div class="form-group" id="entry${index}form">
${btns}
</div>`;
return html;
}
/**
* Build the html for one table row
*
* tweet - object - The twitter data for the cells
* btnHtml - string - A string of HTML to add to the row
*/
function setTableRow(tweet, moreHtml){
let html = `
<th scope="row">${tweet.id}</th>
<td>${tweet.tweet_date}</td>
<td>${tweet.username}</td>
<td>${tweet.text}</td>
<td>${moreHtml}</td>`;
return html;
}
/**
* Build a data matrix for the creating radio button
*
*/
function buildMatrix(){
let matrix = [
["Relevant", 1, true],
["Irrelevant", 0, false],
["Unsure", -1, false]
];
return matrix;
}
/**
* Build the html table row string from the matrix data method
*
* number - number - See index
* data - array - See matrix
*/
function buildTableRow(number, data){
/* A matrix for the radio buttons */
const matrix = buildMatrix();
/* build the html. Notice that these are just string that can tested easily */
const btnsHtml = setRadioBtns(number, matrix);
const rowHtml = setTableRow(data, btnsHtml);
return rowHtml;
}
/* Not Pure Functions */
/**
* Target the table DOM node and add a row to the top, add a class and then
* append the table cell string to the row
*
* NOTE: This method is not a pure function. The side-effects are adding a DOM element
* and add the html to the row. This is easier to test by adding the id to target as a
* parameter. It makes this method generic and it can be use on any table in the product.
* The same with the classname. Isolating the side-effects to untility methods means that
* going through the trouble of unit testing this is minimized.
*
* html - string - The html to add to the table row
*/
function addRowToTable(id, cls, html){
// Find a <table> element with id="js-table-area":
var tableEl = document.getElementById(id);
// Create an empty <tr> element and add it to the 1st position of the table:
var rowEl = tableEl.insertRow(0);
// Add a class to the table row
rowEl.classList.add(cls);
// Append a table row at the top of the table and add html
rowEl.innerHTML = html;
}
/**
* Build the row html string then call the add table row utility method with
* the id anf class name.
*
* NOTE: Because this is using addRowToTable() and that has side-effects this is
* also a method with side-effects. But if all of the pure function are tested and
* the addRowToTable() utility method is tested. Then you assume the is mostly tested
* adding a spy/stub and testing if buildTableRow and addRowToTable where passed the
* correct parameter would be enough to say this was tested.
*
* number - number - See index
* data - array - See matrix
*/
function tableRow(number, data){
let tableId = "js-table-area";
let rowClassname ="tweet-row";
// Build the table row string
const rowHtmlStr = buildTableRow(number, data);
// Add the row to the table
addRowToTable(tableId, rowClassname, rowHtmlStr);
}
/* The js code that maight be in the htmlm page */
/* The tweet data needed for the table row */
const tweet = [
{
id: "0365825348467",
tweet_date: "10/29/2017",
username: "Me253",
text: "Hello World"
},{
id: "9564759674890",
tweet_date: "10/29/2017",
username: "Me253",
text: "Hello Wayan"
}
];
/**
* I only added this because it is being pulled into other codepens.
* If the Codepens doesn't have the js-table-area in the html at the start
* Then it must not be the main codepen where it is used
*/
if(document.getElementById("js-table-area")){
// Add 2 table rows to the table the last should first
tableRow(2, tweet[0]);
tableRow(4, tweet[1]);
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment