Skip to content

Commit

Permalink
Add vertical legend to performance dashboard (#1517)
Browse files Browse the repository at this point in the history
* Add vertical legend to performance dashboard

* Slight tweaks: add ID, improve vertical spacing, missing div, whitespace

---------

Co-authored-by: Ben Sheldon [he/him] <[email protected]>
  • Loading branch information
Wittiest and bensheldon authored Oct 18, 2024
1 parent ecf6df7 commit 581c7ae
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 5 deletions.
3 changes: 3 additions & 0 deletions app/charts/good_job/performance_index_chart.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ def data
display: true,
text: I18n.t("good_job.performance.index.chart_title"),
},
legend: {
vertical: true,
},
},
scales: {
y: {
Expand Down
12 changes: 12 additions & 0 deletions app/frontend/good_job/modules/charts.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import htmlLegendPlugin from "html_legend_plugin";

function renderCharts(animate) {
const charts = document.querySelectorAll('.chart');

for (let i = 0; i < charts.length; i++) {
const chartEl = charts[i];
const chartData = JSON.parse(chartEl.dataset.json);
chartData.options ||= {};

if (chartData.options.plugins?.legend?.vertical) {
chartData.plugins = [htmlLegendPlugin];
chartData.options.plugins = {
...chartData.options.plugins,
legend: {
display: false,
}
}
}
chartData.options.animation = animate;
chartData.options.responsive = true;
chartData.options.maintainAspectRatio = false;
Expand Down
56 changes: 56 additions & 0 deletions app/frontend/good_job/modules/html_legend_plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const generateListItem = (item) => {
const li = document.createElement('li');
li.className = 'd-flex align-items-center text-nowrap mb-2';

const boxSpan = document.createElement('span');
boxSpan.className = 'legend-item-color-box';
boxSpan.style.background = item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';

const textContainer = document.createElement('p');
textContainer.className = 'item-text m-0 small';
textContainer.style.color = item.fontColor;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';

const text = document.createTextNode(item.text);
textContainer.appendChild(text);

li.appendChild(boxSpan);
li.appendChild(textContainer);

return li;
}

const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, _args, _options) {
const {type} = chart.config;
const ul = document.getElementById('chart-legend-ul');

// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}

// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);

items.forEach(item => {
const li = generateListItem(item);
ul.appendChild(li);

li.onclick = () => {
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
});
}
};

export { htmlLegendPlugin as default };
13 changes: 13 additions & 0 deletions app/frontend/good_job/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@
height: 200px;
}

.legend-item-color-box {
display: inline-block;
flex-shrink: 0;
height: 20px;
width: 20px;
border-style: solid;
margin-right: 3px;
}

#chart-legend-container {
height: 200px;
}

/* Break out of a container */
.break-out {
width:100vw;
Expand Down
2 changes: 1 addition & 1 deletion app/views/good_job/jobs/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<%= render 'good_job/shared/filter', title: t("good_job.shared.navbar.jobs"), filter: @filter %>
<%= render 'good_job/shared/chart', chart_data: GoodJob::ScheduledByQueueChart.new(@filter).data %>
<%= render 'good_job/shared/chart_container', chart_data: GoodJob::ScheduledByQueueChart.new(@filter).data %>

<div data-live-poll-region="jobs-table">
<%= render 'good_job/jobs/table', jobs: @filter.records, filter: @filter %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/good_job/performance/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h2 class="pt-3 pb-2"><%= t ".title" %></h2>
</div>

<%= render 'good_job/shared/chart', chart_data: GoodJob::PerformanceIndexChart.new.data %>
<%= render 'good_job/shared/chart_container', chart_data: GoodJob::PerformanceIndexChart.new.data %>

<div class="my-3 card">
<div class="list-group list-group-flush text-nowrap" role="table">
Expand Down
2 changes: 1 addition & 1 deletion app/views/good_job/performance/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
<h2 class="pt-3 pb-2"><%= t ".title" %> - <%= @job_class %></h2>
</div>

<%= render 'good_job/shared/chart', chart_data: GoodJob::PerformanceShowChart.new(@job_class).data %>
<%= render 'good_job/shared/chart_container', chart_data: GoodJob::PerformanceShowChart.new(@job_class).data %>
4 changes: 2 additions & 2 deletions app/views/good_job/shared/_chart.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="py-4" data-live-poll-region="chart">
<div class="chart-wrapper container-fluid">
<div class="" data-live-poll-region="chart">
<div class="chart-wrapper">
<canvas class="chart" data-json="<%= chart_data.to_json %>"></canvas>
</div>
</div>
21 changes: 21 additions & 0 deletions app/views/good_job/shared/_chart_container.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<% vertical_legend = chart_data.dig(:options, :plugins, :legend, :vertical) %>

<div class="container-fluid">
<div class="row d-flex">
<% if vertical_legend %>
<div class="row d-flex">
<div class="col-md-10">
<%= render 'good_job/shared/chart', chart_data: chart_data %>
</div>
<div class="col-md-2 d-flex flex-column">
<div id="chart-legend-container" class="flex-fill overflow-auto">
<ul id="chart-legend-ul" class="list-unstyled p-0 mx-0 py-2">
</ul>
</div>
</div>
</div>
<% else %>
<%= render 'good_job/shared/chart', chart_data: chart_data %>
<% end %>
</div>
</div>

0 comments on commit 581c7ae

Please sign in to comment.