It's D3 time! Been a while since I did D3, but let's have some fun amid the ongoing COVID-19 pandemic. You see, for the past year and a half now, I've been collecting data about Singapore's COVID-19 cases. And it's now time to put all that lovely, lovely data to good use.
Here's a sample of the data in CSV format. The entire dataset can be found 
here.
Day,Imports,MW,SG,Deaths
2020/04/01,20,0,54,0
2020/04/02,8,0,41,1
2020/04/03,0,0,65,1
2020/04/04,6,0,69,1
.
.
.
This is what the data represents:
Day - The date in which the data takes place on. There is one entry for every day of each month.
SG - The number of discovered COVID-19 cases that were Singapore citizens on that day.
MW - The number of discovered COVID-19 cases that were migrant workers on that day.
Imports - The number of discovered COVID-19 cases that were foreigners that traveled to Singapore on that day.
Deaths - The number of people who passed away due to COVID-19 on that day.
Caution
Although this is JavaScript, a server is required because we will be using an asynchronous operation to pull data. We could possibly get around this by turning off security settings, but why take that chance, really?
We begin with some HTML. We will use Verdana for the font and set the 
outline property of all divs to 
red, for some visibility. We have also included the link to the D3 library, with a script tag in the body.
<!DOCTYPE html>
<html>
    <head>
        <title>COVID-19 Dashboard</title>
        <style>
            body
            {
                font-family: verdana;
            }
            div
            {
                outline: 1px solid red;
            }
        </style>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <script>
        </script>
    </body>
</html>
Now, we have a div styled using the CSS class 
dashboardContainer. Within it, we have three divs styled using the CSS classes 
topContainer, 
middleContainer and 
bottomContainer respectively.
<body>
    <div class="dashboardContainer">
        <div class="topContainer">
        </div>
        <div class="middleContainer">
        </div>
        <div class="bottomContainer">
        </div>
    </div>
    <script>
    </script>
</body>
Here are some styles. We set the height of 
dashboardContainer and center it in the screen. 
topContainer, 
middleContainer and 
bottomContainer will each take up full width and float left, with varying heights. 
middleContainer and 
bottomContainer will have the 
margin-top property set.
<style>
    body
    {
        font-family: verdana;
    }
    div
    {
        outline: 1px solid red;
    }
    .dashboardContainer
    {
        width: 800px;
        height: 620px;
        margin: 0 auto 0 auto;
    }
    .topContainer
    {
        width: 100%;
        height: 250px;
        float: left;
    }
    .middleContainer
    {
        width: 100%;
        height: 50px;
        margin-top: 10px;
        float: left;
    }
    .bottomContainer
    {
        width: 100%;
        height: 300px;
        margin-top: 10px;
        float: left;
    }
</style>
And immediately, you get a promising-looking layout!
Then in the divs styled by 
middleContainer and 
bottomContainer, insert two divs each styled by 
leftContainer and 
rightContainer, respectively.
<div class="dashboardContainer">
    <div class="topContainer">
    </div>
    <div class="middleContainer">
        <div class="leftContainer">
        </div>
        <div class="rightContainer">
        </div>
    </div>
    <div class="bottomContainer">
        <div class="leftContainer">
        </div>
        <div class="rightContainer">
        </div>
    </div>
</div>
Now we style these. They float left and right respectively, and have roughly half the width of their parents, and all of the height.
.bottomContainer
{
    width: 100%;
    height: 300px;
    margin-top: 10px;
    float: left;
}
.leftContainer
{
    width: 395px;
    height: 100%;
    float: left;
}
.rightContainer
{
    width: 395px;
    height: 100%;
    float: right;
}
So now, in this div, add a p tag and the following elements within - a string, two buttons and a drop-down list. Add ids for the buttons and the drop-down list.
<div class="middleContainer">
    <div class="leftContainer">
        <p>
            COVID-19 DASHBOARD
            <input type="button" id="btnPrev" value="<<" />
            <select id="ddlPeriod"></select>
            <input type="button" id="btnNext" value=">>" />
        </p>
    </div>
    <div class="rightContainer">
    </div>
</div>
This is a preview of what your main controls on your dashboard will look like.
Add a series of checked checkboxes with strings as labels, in the other div.
<div class="middleContainer">
    <div class="leftContainer">
        <p>
            COVID-19 DASHBOARD
            <input type="button" id="btnPrev" value="<<" />
            <select id="ddlPeriod"></select>
            <input type="button" id="btnNext" value=">>" />
        </p>
    </div>
    <div class="rightContainer">
        <input type="checkbox" checked /> Singapore Citizens
        <input type="checkbox" checked /> Migrant Workers
        <input type="checkbox" checked /> Imports
        <input type="checkbox" checked /> Deaths
    </div>
</div>
It's a little messy and we'll be cleaning this up.
Each of these checkboxes and their labels should go into a div with the class 
statContainer.
<div class="rightContainer">
    <div class="statContainer">
        <input type="checkbox" checked /> Singapore Citizens
    </div>
    <div class="statContainer">
        <input type="checkbox" checked /> Migrant Workers
    </div>
    <div class="statContainer">
        <input type="checkbox" checked /> Imports
    </div>
    <div class="statContainer">
        <input type="checkbox" checked /> Deaths
    </div>
</div>
statContainer will have width and height set, float left and the font will be set as well, but that last part is purely aesthetic.
.rightContainer
{
    width: 395px;
    height: 100%;
    float: right;
}
.statContainer
{
    width: 45%;
    height: 40%;
    float: left;
    font-size: 0.8em;
    font-weight: bold;    
}
And now we have a better layout.
How about we color-code these? Add a class to each of these divs.
<div class="rightContainer">
    <div class="statContainer colSG">
        <input type="checkbox" checked /> Singapore Citizens
    </div>
    <div class="statContainer colMW">
        <input type="checkbox" checked /> Migrant Workers
    </div>
    <div class="statContainer colImports">
        <input type="checkbox" checked /> Imports
    </div>
    <div class="statContainer colDeaths">
        <input type="checkbox" checked /> Deaths
    </div>
</div>
Each of these CSS classes has a different color.
.statContainer
{
    width: 45%;
    height: 40%;
    float: left;
    font-size: 0.8em;
    font-weight: bold;    
}
.colSG { color: #FF0000; }
.colMW { color: #9999FF; }
.colImports { color: #44AA44; }
.colDeaths { color: #999999; }
And here we see that SG data is 
red, MW data is 
pale blue, Imports data is 
green and Deaths data is 
grey. This should be consistent throughout the dashboard.
Now for data!
We'll go right into the script tag. First, we create the object dashboard. In it, we have the following properties.
keys - an array that will hold all the possible month-year combinations of the dataset, so as to be able to traverse the array 
covidData easily.
currentKey - a value that will be one of the values within keys. It defaults to undefined.
covidData - an array that holds your entire dataset.
totalSG - the pre-calculated sum of all Singapore Citizen cases. The initial value is 0.
totalMW - the pre-calculated sum of all migrant worker cases. The initial value is 0.
totalImports - the pre-calculated sum of all imported cases. The initial value is 0.
totalDeaths - the pre-calculated sum of all deaths. The initial value is 0.
<script>
    let dashboard = 
    {
        keys: [],
        currentKey: undefined,
        covidData: [],
        totalSG: 0,
        totalMW: 0,
        totalImports: 0,
        totalDeaths: 0
    };
</script>
Here, we use the 
csv() method of the 
d3 object. Within it, we pass the name of our CSV file, which is 
covid19.csv, and a callback. 
data is a parameter which represents the CSV data extracted from the file. 
data consists of an array of objects. Each object represents a line in the CSV data.
<script>
    let dashboard = 
    {
        keys: [],
        currentKey: undefined,
        covidData: [],
        totalSG: 0,
        totalMW: 0,
        totalImports: 0,
        totalDeaths: 0
    };
    d3.csv("covid19.csv", function(data) 
    {
    });
</script>
Here, we are going to manipulate the data into a form that our dashboard can easily parse. We first use a 
For loop to iterate through the contents of 
data. We then define the variable, 
key, and use the 
getKey() method of the 
dashboard object, passing in the 
Day property of the current object.
d3.csv("covid19.csv", function(data) 
{
    for (var i = 0; i < data.length; i++) 
    {
        var key = dashboard.getKey(data[i].Day);
    }
});
Now we define the 
getKey() method. It will accept a string, 
x, as a parameter. This is the date string which is the 
Day property.
let dashboard = 
{
    keys: [],
    currentKey: undefined,
    covidData: [],
    totalSG: 0,
    totalMW: 0,
    totalImports: 0,
    totalDeaths: 0,
    getKey: function(x) 
    {
    }
};
Each Day string is in the format YYYY/MM/DD. So we first define a variable, 
elems, as an array derived from running 
x through the 
split() method and using "/" as an argument. The variable 
year is the first element of 
elems. The variable 
month is the second element of 
elems, minus 1 because we are going to use this value as a pointer to an array. The array is 
monthNames, and it contains all the month names of the calendar.
let dashboard = 
{
    keys: [],
    currentKey: undefined,
    covidData: [],
    totalSG: 0,
    totalMW: 0,
    totalImports: 0,
    totalDeaths: 0,
    getKey: function(x) 
    {
        var elems = x.split("/");
        var year = elems[0];
        var month = parseInt(elems[1]) - 1;
        var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    }
};
We return the element of 
monthNames pointed to by 
month, and 
year, in a string. So if we passed in "2020/04/03" as an argument, we would get "Apr 2020".
let dashboard = 
{
    keys: [],
    currentKey: undefined,
    covidData: [],
    totalSG: 0,
    totalMW: 0,
    totalImports: 0,
    totalDeaths: 0,
    getKey: function(x) 
    {
        var elems = x.split("/");
        var year = elems[0];
        var month = parseInt(elems[1]) - 1;
        var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
        return monthNames[month] + " " + year;
    }
};
Now that we've defined 
getKey(), let's move on. We use an 
If block to check if the current value of 
currentKey is undefined.
d3.csv("covid19.csv", function(data) 
{
    for (var i = 0; i < data.length; i++) 
    {
        var key = dashboard.getKey(data[i].Day);
        if (dashboard.currentKey == undefined) 
        {
        }
        else
        {
        }    
    }
});
If so, we set 
currentKey to the value of 
key, and create a new sub-array under 
covidData using 
key as the key. And then we push 
key into 
keys.
d3.csv("covid19.csv", function(data) 
{
    for (var i = 0; i < data.length; i++) 
    {
        var key = dashboard.getKey(data[i].Day);
        if (dashboard.currentKey == undefined) 
        {
            dashboard.currentKey = key;
            dashboard.covidData[key] = [];
            dashboard.keys.push(key);
        }
        else
        {
        }    
    }
});
Otherwise, we first check if 
currentKey is equal to 
key. If not, we repeat what we did earlier.
d3.csv("covid19.csv", function(data) 
{
    for (var i = 0; i < data.length; i++) 
    {
        var key = dashboard.getKey(data[i].Day);
        if (dashboard.currentKey == undefined) 
        {
            dashboard.currentKey = key;
            dashboard.covidData[key] = [];
            dashboard.keys.push(key);
        }
        else
        {
            if (dashboard.currentKey != key)
            {
                dashboard.currentKey = key;
                dashboard.covidData[key] = [];
                dashboard.keys.push(key);
            }
        }    
    }
});
Now, we start pushing the entire contents of the current element of 
data into that sub-array. What this does is that this will segregate data into sub-arrays delineated by month-year combinations.
d3.csv("covid19.csv", function(data) 
{
    for (var i = 0; i < data.length; i++) 
    {
        var key = dashboard.getKey(data[i].Day);
        if (dashboard.currentKey == undefined) 
        {
            dashboard.currentKey = key;
            dashboard.covidData[key] = [];
            dashboard.keys.push(key);
        }
        else
        {
            if (dashboard.currentKey != key)
            {
                dashboard.currentKey = key;
                dashboard.covidData[key] = [];
                dashboard.keys.push(key);
            }
        }    
        dashboard.covidData[key].push({"sg": data[i].SG, "imports": data[i].Imports, "mw": data[i].MW, "deaths": data[i].Deaths});
    }
});
At this moment, we will take advantage of the 
For loop to increment the values of 
totalSG, 
totalMW, 
totalImports and 
totalDeaths accordingly. At the end of it, we should get the totals of the entire dataset. These will be useful later!
d3.csv("covid19.csv", function(data) 
{
    for (var i = 0; i < data.length; i++) 
    {
        var key = dashboard.getKey(data[i].Day);
        if (dashboard.currentKey == undefined) 
        {
            dashboard.currentKey = key;
            dashboard.covidData[key] = [];
            dashboard.keys.push(key);
        }
        else
        {
            if (dashboard.currentKey != key)
            {
                dashboard.currentKey = key;
                dashboard.covidData[key] = [];
                dashboard.keys.push(key);
            }
        }    
        dashboard.covidData[key].push({"sg": data[i].SG, "imports": data[i].Imports, "mw": data[i].MW, "deaths": data[i].Deaths});
        dashboard.totalSG = dashboard.totalSG + parseInt(data[i].SG);
        dashboard.totalMW = dashboard.totalMW + parseInt(data[i].MW);
        dashboard.totalImports = dashboard.totalImports + parseInt(data[i].Imports);
        dashboard.totalDeaths = dashboard.totalDeaths + parseInt(data[i].Deaths);
    }
});
Outside of the 
For loop, define variable 
ddlPeriod and use the 
select() method of the 
d3 object to get the drop-down list 
ddlPeriod.
    dashboard.totalSG = dashboard.totalSG + parseInt(data[i].SG);
    dashboard.totalMW = dashboard.totalMW + parseInt(data[i].MW);
    dashboard.totalImports = dashboard.totalImports + parseInt(data[i].Imports);
    dashboard.totalDeaths = dashboard.totalDeaths + parseInt(data[i].Deaths);
}
var ddlPeriod = d3.select("#ddlPeriod");
Now we use the keys object (which we should have populated with all the possible month-year combinations in the dataset) to fill the drop-down list with option tags.
var ddlPeriod = d3.select("#ddlPeriod");
ddlPeriod.selectAll("option")
.data(dashboard.keys)
.enter()
.append("option");
We'll use the actual value as the text, but use the element number as the value. And by default, we will select the first value.
ddlPeriod.selectAll("option")
.data(dashboard.keys)
.enter()
.append("option")
.property("selected", function(d, i) 
{
    return i == 0;
})
.attr("value", function(d, i) 
{
    return i;
})
.text(function(d) 
{
    return d;
});
And see now, we've populated the drop-down list!
We follow up by calling the 
drawCharts() method of the 
dashboard object.
ddlPeriod.selectAll("option")
.data(dashboard.keys)
.enter()
.append("option")
.property("selected", function(d, i) 
{
    return i == 0;
})
.attr("value", function(d, i) 
{
    return i;
})
.text(function(d) 
{
    return d;
});
dashboard.drawCharts();
We are going to define the 
drawCharts() method soon. But first, make sure that it fires off whenever the checkboxes are clicked, using the 
onclick attribute. We will also add an extra class to each containing div so that it is easily identifiable by the drawCharts() method.
<div class="rightContainer">
    <div class="statContainer cb_sg colSG">
        <input type="checkbox" onclick="dashboard.drawCharts();" checked/> Singapore Citizens
    </div>
    <div class="statContainer cb_mw colMW">
        <input type="checkbox" onclick="dashboard.drawCharts();" checked/> Migrant Workers
    </div>
    <div class="statContainer cb_imports colImports">
        <input type="checkbox" onclick="dashboard.drawCharts();" checked/> Imports
    </div>
    <div class="statContainer cb_deaths colDeaths">
        <input type="checkbox" onclick="dashboard.drawCharts();" checked/> Deaths
    </div>    
</div>
Let us define the 
drawCharts() method now.
getKey: function(x) 
{
    var elems = x.split("/");
    var year = elems[0];
    var month = parseInt(elems[1]) - 1;
    var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    return monthNames[month] + " " + year;
},
drawCharts: function() 
{
}
Here, we select 
ddlPeriod. Then we define 
data as a subset of 
covidData, using the selected value of 
ddlPeriod. It is an integer, but when we use that integer as a pointer to reference 
covidData, we get the key! And it's this key that we use to get the data of, say, April 2020.
drawCharts: function() 
{
    var ddlPeriod = d3.select("#ddlPeriod");
    var data = this.covidData[this.keys[ddlPeriod.node().value]];
}
Then we'll define 
sg, 
mw, 
imports and 
deaths as Boolean values based on which checkboxes are checked. So if the user only wishes to view Deaths and Imports, he would only check thse checkboxes. And the dashbard will reflect thse choices.
drawCharts: function() 
{
    var ddlPeriod = d3.select("#ddlPeriod");
    var sg = d3.select(".cb_sg input[type=checkbox]").property("checked"); 
    var mw = d3.select(".cb_mw input[type=checkbox]").property("checked");
    var imports = d3.select(".cb_imports input[type=checkbox]").property("checked");
    var deaths = d3.select(".cb_deaths input[type=checkbox]").property("checked");
    var data = this.covidData[this.keys[ddlPeriod.node().value]];
}
Then we will call the 
drawLineChart(), 
drawDonutChart() and 
drawBarChart() methods, passing in the variables we defined, as arguments. Note that 
drawDonutChart() does not use deaths.
drawCharts: function() 
{
    var ddlPeriod = d3.select("#ddlPeriod");
    var sg = d3.select(".cb_sg input[type=checkbox]").property("checked"); 
    var mw = d3.select(".cb_mw input[type=checkbox]").property("checked");
    var imports = d3.select(".cb_imports input[type=checkbox]").property("checked");
    var deaths = d3.select(".cb_deaths input[type=checkbox]").property("checked");
    var data = this.covidData[this.keys[ddlPeriod.node().value]];
    dashboard.drawLineChart(data, sg, mw, imports, deaths);
    dashboard.drawDonutChart(data, sg, mw, imports);
    dashboard.drawBarChart(data, sg, mw, imports, deaths);
}
And we will just populate the 
dashboard object with these methods.
drawCharts: function() 
{
    var ddlPeriod = d3.select("#ddlPeriod");
    var sg = d3.select(".cb_sg input[type=checkbox]").property("checked"); 
    var mw = d3.select(".cb_mw input[type=checkbox]").property("checked");
    var imports = d3.select(".cb_imports input[type=checkbox]").property("checked");
    var deaths = d3.select(".cb_deaths input[type=checkbox]").property("checked");
    var data = this.covidData[this.keys[ddlPeriod.node().value]];
    dashboard.drawLineChart(data, sg, mw, imports, deaths);
    dashboard.drawDonutChart(data, sg, mw, imports);
    dashboard.drawBarChart(data, sg, mw, imports, deaths);
},
drawLineChart: function(data, sg, mw, imports, deaths) 
{
},
drawDonutChart: function(data, sg, mw, imports) 
{
},
drawBarChart: function(data, sg, mw, imports, deaths) 
{
}
After calling the 
drawCharts() method, make sure it runs whenever 
ddlPeriod changes in value.
dashboard.drawCharts();
d3.select("#ddlPeriod").on("change", function() { dashboard.drawCharts(); });
When the buttons are clicked, we need to run 
prev() and 
next() methods before running 
drawCharts().
dashboard.drawCharts();
d3.select("#ddlPeriod").on("change", function() { dashboard.drawCharts(); });
d3.select("#btnPrev").on("click", function() { dashboard.prev(); dashboard.drawCharts(); });
d3.select("#btnNext").on("click", function() { dashboard.next(); dashboard.drawCharts(); });
Now let us create the 
prev() and 
next() methods.
getKey: function(x) 
{
    var elems = x.split("/");
    var year = elems[0];
    var month = parseInt(elems[1]) - 1;
    var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    return monthNames[month] + " " + year;
},
prev: function()
{
},
next: function()
{
},
drawCharts: function() 
{
Now it's just a matter of getting the current value of 
ddlPeriod, and set it to 
currentVal.
prev: function()
{
    var currentVal = parseInt(d3.select("#ddlPeriod").node().value);
},
next: function()
{
},
Since this basically sets the pointer to the previous value and the first value is 0, we only proceed if 
currentVal is greater than 0.
prev: function()
{
    var currentVal = parseInt(d3.select("#ddlPeriod").node().value);
    if (currentVal > 0)
    {
    }
},
next: function()
{
},
We decrement 
currentVal, and set the value of 
ddlPeriod to 
currentVal.
prev: function()
{
    var currentVal = parseInt(d3.select("#ddlPeriod").node().value);
    if (currentVal > 0)
    {
        currentVal = currentVal - 1;
        d3.select("#ddlPeriod").property("value", currentVal);
    }
},
next: function()
{
},
Now we do pretty much the same for 
next(), except that we increment 
currentVal, and only if 
currentVal is lesser than the maximum value.
prev: function()
{
    var currentVal = parseInt(d3.select("#ddlPeriod").node().value);
    if (currentVal > 0)
    {
        currentVal = currentVal - 1;
        d3.select("#ddlPeriod").property("value", currentVal);
    }
},
next: function()
{
    var currentVal = parseInt(d3.select("#ddlPeriod").node().value);
    if (currentVal < this.keys.length - 1)
    {
        currentVal = currentVal + 1;
        d3.select("#ddlPeriod").property("value", currentVal);
    }
},
Now if you click those buttons, you can see the selected value in the drop-down list change! And once you reach "Apr 2020" or "Aug 2021" (the first and last values respectively), the value no longer changes.
That's all for now. But we have created a good framework for what we're about to do next.
Next
Drawing the line chart.