A Pen by Andrew Pagan on CodePen.
Created
July 29, 2021 20:02
-
-
Save drewkiimon/0aadfe943ee344e0bbffc8434ff0f6e2 to your computer and use it in GitHub Desktop.
[Highcharts] Grouped Stacked Bar Chart v2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const testJSON = [ | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Jumping", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Dividing Cups", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Add within 1000", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Represent multiplication on the number line", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Subtract within 1000", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Unit fractions on the number line", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Fractions on the number line", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Find 1 on the number line", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Dividing A lot of Cups", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Identify quadrilaterals", | |
"Current Skill Mastery Level": "Familiar" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Write numbers in different forms", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Jumping", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Dividing Cups", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Add within 1000", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Represent multiplication on the number line", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Subtract within 1000", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Unit fractions on the number line", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Fractions on the number line", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Find 1 on the number line", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Dividing A lot of Cups", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Round to nearest 10 or 100", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Jumping", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Dividing Cups", | |
"Current Skill Mastery Level": "Familiar" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Compare fractions with the same numerator or denominator", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Add within 1000", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Represent multiplication on the number line", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Subtract within 1000", | |
"Current Skill Mastery Level": "Proficient" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Unit fractions on the number line", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Fractions on the number line", | |
"Current Skill Mastery Level": "Mastered" | |
}, | |
{ | |
"School Name": "Oceana High School", | |
"Course Name": "Ready to Party", | |
"Skill Name": "Dividing A lot of Cups", | |
"Current Skill Mastery Level": "Familiar" | |
} | |
]; | |
const formatDataForSeries = (data) => { | |
const courses = {}; | |
const formattedData = []; | |
const returnedSkills = []; | |
const returnedCourses = []; | |
for (var i = 0; i <= data.length - 1; i++) { | |
const course = data[i]["Course Name"]; | |
const skill = data[i]["Skill Name"]; | |
const mastery = data[i]["Current Skill Mastery Level"]; | |
if (!courses[course]) { | |
courses[course] = {}; | |
returnedCourses.push(course); | |
} | |
if (!courses[course][skill]) { | |
courses[course][skill] = {}; | |
returnedSkills.push(skill); | |
} | |
if (!courses[course][skill][mastery]) { | |
courses[course][skill][mastery] = 0; | |
} | |
courses[course][skill][mastery]++; | |
} | |
const courseNames = Object.keys(courses); | |
const helper = {}; | |
for (var i = 0; i <= courseNames.length - 1; i++) { | |
let c = courseNames[i]; | |
let skills = Object.keys(courses[c]); | |
console.log(c, skills); | |
for (var j = 0; j <= skills.length - 1; j++) { | |
let s = skills[j]; | |
let coolData = courses[c][s]; | |
let levels = Object.keys(coolData); | |
if (!helper["Mastered"]) { | |
helper["Mastered"] = { | |
name: "Mastered", | |
data: [], | |
color: "#808080" | |
}; | |
} | |
if (!helper["Familiar"]) { | |
helper["Familiar"] = { | |
name: "Familiar", | |
data: [], | |
color: "#D3D3D3" | |
}; | |
} | |
if (!helper["Proficient"]) { | |
helper["Proficient"] = { | |
name: "Proficient", | |
data: [], | |
color: "#A9A9A9" | |
}; | |
} | |
if (!helper["Attempted this skill"]) { | |
helper["Attempted this skill"] = { | |
name: "Attempted this skill", | |
data: [], | |
color: "#F5F5F5" | |
}; | |
} | |
helper["Mastered"].data.push(coolData["Mastered"] || 0); | |
helper["Familiar"].data.push(coolData["Familiar"] || 0); | |
helper["Proficient"].data.push(coolData["Proficient"] || 0); | |
helper["Attempted this skill"].data.push( | |
coolData["Attempted this skill"] || 0 | |
); | |
} | |
} | |
console.log(helper); | |
console.log(courses); | |
return [returnedCourses, returnedSkills, Object.values(helper)]; | |
}; | |
const [courses, skills, data] = formatDataForSeries(testJSON); | |
console.log(courses, skills, data); | |
// const skills = [ | |
// "Algebra", | |
// "Chemistry", | |
// "Programming", | |
// "How to Smile", | |
// "How to listen", | |
// "Fib Numbers" | |
// ]; | |
const images = [ | |
"https://i.pinimg.com/originals/24/73/d9/2473d94cb7d607b461ecece38a0100bf.jpg", | |
"https://i.ibb.co/b3wVNYP/graph.png", | |
"https://tests4geeks.com/content/img/smp/java-programming-test-answer-1.png", | |
"https://smb.ibsrv.net/imageresizer/image/blog_images/1200x1200/59846/176287/0044181001582748537.jpg", | |
"https://www.incimages.com/uploaded_files/image/1920x1080/getty_464672063_2000160020009280233_339359.jpg", | |
"https://math.temple.edu/~reich/Fib/fibfamily.gif" | |
]; | |
const chart = Highcharts.chart("container", { | |
chart: { | |
type: "column", | |
animation: false, | |
backgroundColor: "#f7f8fa", | |
events: { | |
load: function () { | |
const bands = document.getElementsByClassName("highcharts-plot-band"); | |
for (var i = 0; i <= bands.length - 1; i++) { | |
let band = bands[i]; | |
let bandElements = band.getBoundingClientRect(); | |
let bandWidth = bandElements.width; | |
const bandLabel = document.getElementById("band" + (i + 1)); | |
bandLabel.style.width = bandWidth - 16 + "px"; | |
} | |
}, | |
redraw: function () { | |
const bands = document.getElementsByClassName("highcharts-plot-band"); | |
for (var i = 0; i <= bands.length - 1; i++) { | |
let band = bands[i]; | |
let bandElements = band.getBoundingClientRect(); | |
let bandWidth = bandElements.width; | |
const bandLabel = document.getElementById("band" + (i + 1)); | |
bandLabel.style.width = bandWidth - 16 + "px"; | |
} | |
} | |
} | |
}, | |
credits: { enabled: false }, | |
title: { | |
text: "" | |
}, | |
tooltip: { | |
backgroundColor: null, | |
borderWidth: 0, | |
hideDelay: 100, | |
shadow: false, | |
useHTML: true, | |
outside: true, | |
// positioner: function (_, _, point) { | |
// console.log(this); | |
// console.log(point); | |
// }, | |
formatter: function () { | |
const masteryLevel = this.series.name; | |
const { color } = this.point; | |
return ` | |
<span class="tooltip-header">${skills[this.x]}</span><br/> | |
<span class="tooltip-subheader">Identifying coordinates</span><br/> | |
<div class="tooltip-info"> | |
<div class="tooltip-box" style="background-color: ${color}"></div><span class="tooltip-category">${masteryLevel}</span>: <span class="tooltip-value">${ | |
this.y | |
}</span> | |
</div> | |
<img class="tooltip-image" src="${images[this.x]}" alt="graph image"/> | |
`; | |
}, | |
style: { | |
padding: 0 | |
} | |
}, | |
plotOptions: { | |
column: { | |
groupPadding: 0, | |
pointPadding: 0.15, | |
borderWidth: 0, | |
stacking: "normal", | |
states: { | |
inactive: { | |
enabled: false | |
}, | |
hover: { | |
color: "#1865F2" | |
} | |
} | |
}, | |
series: { | |
pointWidth: 26 | |
} | |
}, | |
legend: { | |
align: "right", | |
verticalAlign: "top", | |
layout: "vertical", | |
title: { text: "MASTERY LEVELS" }, | |
backgroundColor: "white", | |
padding: 20, | |
x: -50, | |
y: 0, | |
symbolPadding: 15, | |
symbolRadius: 2, | |
itemMarginBottom: 8, | |
borderColor: "lightgray", | |
borderWidth: 1, | |
borderRadius: 2, | |
shadow: true | |
}, | |
xAxis: { | |
title: { | |
text: "UNITS", | |
align: "right" | |
}, | |
labels: { | |
enabled: false | |
}, | |
plotLines: [ | |
{ | |
color: "black", | |
width: 1, | |
value: 2.5, | |
dashStyle: "ShortDot", | |
zIndex: 10 | |
}, | |
{ | |
color: "black", | |
width: 1, | |
value: 4.5, | |
dashStyle: "ShortDot", | |
zIndex: 10 | |
} | |
], | |
plotBands: [ | |
{ | |
borderWidth: 1, | |
from: -0.5, | |
to: 2.5, | |
color: "#f7f8fa", | |
label: { | |
align: "left", | |
text: | |
"<div id='band1' class='band'><b>SYSTEM OF EQUATIONS</b><br/><span>4 Skills</span></div>", | |
useHTML: true, | |
verticalAlign: "bottom" | |
} | |
}, | |
{ | |
borderWidth: 1, | |
from: 2.5, | |
to: 4.5, | |
color: "#f7f8fa", | |
label: { | |
align: "left", | |
text: | |
"<div id='band2' class='band'><b>FUNCTIONS</b><br/><span>2 Skills</span></div>", | |
useHTML: true, | |
verticalAlign: "bottom" | |
} | |
}, | |
{ | |
borderWidth: 1, | |
from: 4.5, | |
to: 5.5, | |
color: "#f7f8fa", | |
label: { | |
align: "left", | |
text: | |
"<div id='band3' class='band'><b>SEQUENCES</b><br/><span>1 Skill</span></div>", | |
useHTML: true, | |
verticalAlign: "bottom" | |
} | |
} | |
], | |
tickWidth: 0 | |
}, | |
yAxis: { | |
title: { enabled: false }, | |
allowDecimals: false, | |
// min: 2 * 10, | |
// max: 300 * 1.1, // gives some buffer at top | |
gridLineWidth: 0, | |
tickAmount: 13, | |
tickWidth: 1, | |
tickLength: 16, | |
offset: 20, | |
labels: { | |
align: "left", | |
x: 4, | |
y: 4 | |
} | |
}, | |
series: data | |
// [ | |
// // ["Skill 1", ...] | |
// { | |
// name: "Mastered", | |
// data: [12, 8, 34, 32, 64, 72], | |
// color: "#808080" | |
// }, | |
// { | |
// name: "Proficient", | |
// data: [95, 88, 30, 122, 25, 45], | |
// color: "#A9A9A9" | |
// }, | |
// { | |
// name: "Familiar", | |
// data: [100, 80, 70, 111, 25, 13], | |
// color: "#D3D3D3" | |
// }, | |
// { | |
// name: "Attempted this skill", | |
// data: [10, 22, 2, 48, 51, 66], | |
// color: "#F5F5F5" | |
// } | |
// ] | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.highcharts-tooltip > span { | |
background: rgba(255, 255, 255, 0.85); | |
border: 1px solid silver; | |
border-radius: 3px; | |
box-shadow: 1px 1px 2px #888; | |
padding: 24px; | |
} | |
.tooltip-header { | |
font-family: Lato; | |
font-size: 12px; | |
font-style: normal; | |
font-weight: 700; | |
line-height: 16px; | |
letter-spacing: 0.6px; | |
text-align: left; | |
color: rgba(33, 36, 44, 0.64); | |
margin-bottom: 4px; | |
} | |
.tooltip-subheader { | |
font-family: Lato; | |
font-size: 16px; | |
font-style: normal; | |
font-weight: 700; | |
line-height: 22px; | |
letter-spacing: 0em; | |
text-align: left; | |
color: background: rgba(0, 0, 0, 1); | |
margin-bottom: 8px; | |
} | |
.tooltip-info { | |
height: 8px; | |
border-bottom: 1px solid rgba(33, 36, 44, 0.16); | |
padding-bottom: 24px; | |
} | |
.tooltip-box { | |
display: inline-block; | |
width: 8px; | |
height: 8px; | |
border-radius: 2px; | |
margin-right: 8px; | |
} | |
.tooltip-category { | |
font-family: Lato; | |
font-size: 12px; | |
font-style: normal; | |
font-weight: 700; | |
line-height: 16px; | |
letter-spacing: 0em; | |
text-align: left; | |
} | |
.tooltip-value { | |
font-family: Lato; | |
font-size: 12px; | |
font-style: normal; | |
font-weight: 400; | |
line-height: 16px; | |
letter-spacing: 0em; | |
text-align: left; | |
} | |
.tooltip-image { | |
max-width: 230px; | |
margin-top: 14px; | |
} | |
.band { | |
overflow: hidden; | |
text-overflow: ellipsis; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment