|
/* |
|
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]); |
|
} |