Skip to content

Commit

Permalink
Merge pull request #333 from GTBitsOfGood/bugfix/chartFixes
Browse files Browse the repository at this point in the history
Bugfix/chart fixes
  • Loading branch information
thanasis457 authored Jan 5, 2025
2 parents 8287284 + c1668bf commit f8d9a13
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 151 deletions.
3 changes: 1 addition & 2 deletions src/components/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,7 @@ const Map = (props) => {
style={{ display: "flex", alignItems: "center" }}
>
<div style={{ marginRight: "8px", marginBottom: "6px" }}> </div>
{
console.log((filtersData))}

{Object.entries(filtersData)
.filter(
([key, value]: any[]) =>
Expand Down
10 changes: 5 additions & 5 deletions src/components/subcomponents/ProviderInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const ProviderInfo = (props) => {
case "donut":
return (
<Collapsible
label={type}
label={data.title}
style={{
maxWidth: "1000px",
marginLeft: "auto",
Expand All @@ -72,7 +72,7 @@ const ProviderInfo = (props) => {
case "progress":
return (
<Collapsible
label={type}
label={data.title}
style={{
maxWidth: "1000px",
marginLeft: "auto",
Expand All @@ -85,21 +85,21 @@ const ProviderInfo = (props) => {
units={data.data.units}
buttonLink={data.data.buttonLink}
buttonLabel={data.data.buttonLabel}
showNumber={data.data.showNumber}
></ProgressBar>
</Collapsible>
);
case "line":
return (
<Collapsible
label={type}
label={data.title}
style={{
maxWidth: "1000px",
marginLeft: "auto",
marginRight: "auto",
}}
>
<LineChart
title={data.title}
data={data.data.lineData}
></LineChart>
</Collapsible>
Expand Down Expand Up @@ -254,7 +254,7 @@ const ProviderInfo = (props) => {
if (
index !==
props.item[category.id].length -
1
1
) {
return (
<div className="modal-text">
Expand Down
3 changes: 3 additions & 0 deletions src/components/subcomponents/chartcomponents/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React from "react";

const Button = ({ link, label }) => {
if (link.indexOf(".") === -1 || !label) {
return null;
}
return (
<a href={link} target="_blank">
<button
Expand Down
64 changes: 40 additions & 24 deletions src/components/subcomponents/chartcomponents/ChartComponentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface DonutData {
}

interface LineData {
x: string;
x: number;
y: number;
}

Expand All @@ -33,11 +33,7 @@ interface ChartForm {
data: ChartData;
}

const ChartComponentForm = ({
chartState,
setChartState,
deleteComponent,
}) => {
const ChartComponentForm = ({ chartState, setChartState, deleteComponent }) => {
const handleTypeChange = (type: ChartType) => {
setChartState({
type,
Expand All @@ -46,6 +42,8 @@ const ChartComponentForm = ({
});
};

const randomId = Math.random();

const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setChartState({ ...chartState, title: e.target.value });
};
Expand Down Expand Up @@ -80,20 +78,21 @@ const ChartComponentForm = ({
) => {
const newData =
chartState.type === "donut"
? [...(chartState.data.donutData || [])]
: [...(chartState.data.lineData || [])];
newData[index] = { ...newData[index], [key]: value };
? chartState.data.donutData.map((row) => ({ ...row }))
: chartState.data.lineData.map((row) => ({ ...row }));

//handles update to percentage column for DonutData
if (newData.length > 0 && chartState.type === "donut") {
let sum = 0;
newData.forEach((row) => {
sum += row.number;
});
newData[index][key] = value !== "" ? parseFloat(value as string) : "";

if (chartState.type === "donut") {
const total = newData.reduce((sum, row) => sum + row.number, 0);
newData.forEach((row) => {
row.percentage = ((row.number / sum) * 100).toFixed(1) + "%";
row.percentage =
total > 0
? ((row.number / total) * 100).toFixed(1) + "%"
: "0%";
});
}

setChartState({
...chartState,
data: {
Expand Down Expand Up @@ -169,6 +168,17 @@ const ChartComponentForm = ({
parseFloat(e.target.value)
)
}
onBlur={(e) => {
if (e.target.value === "") {
handleArrayDataChange(
index,
"number",
0
);
e.target.value = "0";
}
}}
min={0}
required
/>
</td>
Expand Down Expand Up @@ -205,7 +215,7 @@ const ChartComponentForm = ({
<tr key={index}>
<td>
<input
type="text"
type="number"
value={row.x}
onChange={(e) =>
handleArrayDataChange(
Expand All @@ -225,7 +235,7 @@ const ChartComponentForm = ({
handleArrayDataChange(
index,
"y",
parseFloat(e.target.value)
e.target.value
)
}
required
Expand Down Expand Up @@ -337,14 +347,15 @@ const ChartComponentForm = ({
<input
id="current"
type="number"
value={chartState.data.current || ""}
value={chartState.data.current}
onChange={(e) =>
handleDataChange(
"current",
parseFloat(e.target.value)
)
}
placeholder="ex. 10"
min={0}
/>
</div>
<div className="field" style={{ width: "30%" }}>
Expand All @@ -354,14 +365,15 @@ const ChartComponentForm = ({
<input
id="goal"
type="number"
value={chartState.data.total || ""}
value={chartState.data.total}
onChange={(e) =>
handleDataChange(
"total",
parseFloat(e.target.value)
)
}
placeholder="ex. 1000"
min={0}
/>
</div>
<div className="radio-group">
Expand Down Expand Up @@ -492,7 +504,7 @@ const ChartComponentForm = ({
</div>
);
default:
return <></>
return <></>;
}
};

Expand All @@ -502,7 +514,7 @@ const ChartComponentForm = ({
<label>
<input
type="radio"
name="chartType"
name={`chartType-${randomId}`}
value="donut"
checked={chartState.type === "donut"}
onChange={() => handleTypeChange("donut")}
Expand All @@ -512,7 +524,7 @@ const ChartComponentForm = ({
<label>
<input
type="radio"
name="chartType"
name={`chartType-${randomId}`}
value="progress"
checked={chartState.type === "progress"}
onChange={() => handleTypeChange("progress")}
Expand All @@ -522,7 +534,7 @@ const ChartComponentForm = ({
<label>
<input
type="radio"
name="chartType"
name={`chartType-${randomId}`}
value="line"
checked={chartState.type === "line"}
onChange={() => handleTypeChange("line")}
Expand All @@ -541,6 +553,10 @@ const ChartComponentForm = ({
/>
</div>
{renderFields()}
<div>
<h4>Current Data:</h4>
<pre>{JSON.stringify(chartState, null, 2)}</pre>
</div>
<div className="footer">
<button
type="button"
Expand Down
69 changes: 26 additions & 43 deletions src/components/subcomponents/chartcomponents/DonutChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,11 @@ const formatNumberWithCommas = (number: number): string => {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};

/*
Props:
data : [{label : string, number : number, percentage : string}],
EX:
const data = [
{ label: "Equipment", number: 7140, percentage: "34%" },
{ label: "Programs", number: 5670, percentage: "27%" },
{ label: "Technology", number: 4410, percentage: "21%" },
{ label: "Uniforms", number: 3780, percentage: "18%" },
];
buttonLink : string,
buttonLabel : string
*/
const DonutChart = ({ data, buttonLink, buttonLabel }) => {
const svgRef = useRef(null);
buttonLink = !/^https?:\/\//i.test(buttonLink)
? "http://" + buttonLink
: buttonLink; //make sures external link has proper formatting
: buttonLink; // Ensures external link has proper formatting

useEffect(() => {
const width = parseInt(d3.select(svgRef.current).style("width"));
Expand All @@ -46,7 +33,7 @@ const DonutChart = ({ data, buttonLink, buttonLabel }) => {
const opacityScale = d3
.scaleLinear()
.domain([0, data.length - 1])
.range([1, 0.2]);
.range([1, 0.3]);

// Method to process and sort the data
const pie = d3
Expand All @@ -67,15 +54,15 @@ const DonutChart = ({ data, buttonLink, buttonLabel }) => {
.append("g")
.attr("transform", `translate(${width / 2}, ${height / 3})`);

// Sets the proper color for each slice, the pie method sorts from descending so largest value has full opacity
// Draws the donut chart slices
svgGroup
.selectAll("path")
.data(pie(data))
.enter()
.append("path")
.attr("d", arc as any)
.attr("fill", (_, i): any => {
const opacity = opacityScale(i);
.attr("fill", (d): any => {
const opacity = opacityScale(d.index);
return d3.rgb(baseColor.r, baseColor.g, baseColor.b, opacity);
})
.attr("stroke", "#fff")
Expand Down Expand Up @@ -107,23 +94,24 @@ const DonutChart = ({ data, buttonLink, buttonLabel }) => {
const outerArc = d3
.arc()
.innerRadius(radius * 1)
.outerRadius(radius * 1.1);
.outerRadius(radius * 1.3);

// Prepare data for outer labels with centroids
let outerLabelsData = pie(data).map((d) => {
const pos = outerArc.centroid(d as any);
return { ...d, x: pos[0], y: pos[1] };
});

// Defines outer info label group and controls their positioning
// Render adjusted outer labels
const outerLabel = svgGroup
.selectAll(".outer-label")
.data(pie(data))
.data(outerLabelsData)
.enter()
.append("g")
.attr("class", "outer-label")
.attr("transform", function (d) {
const pos = outerArc.centroid(d as any);
const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
pos[0] = radius * 1.45 * (midAngle < Math.PI ? 1 : -1);
return `translate(${pos})`;
});

// Adds and controls the label from inputted data
.attr("transform", (d) => `translate(${d.x}, ${d.y})`);

// Add text labels
outerLabel
.append("text")
.text((d) => d.data.label)
Expand All @@ -132,7 +120,7 @@ const DonutChart = ({ data, buttonLink, buttonLabel }) => {
.attr("font-size", "0.15rem")
.attr("fill", "#7C7C7C");

// Adds and controls the number from inputted data
// Add numbers below labels
outerLabel
.append("text")
.text((d) => `($${formatNumberWithCommas(d.data.number)})`)
Expand All @@ -141,25 +129,20 @@ const DonutChart = ({ data, buttonLink, buttonLabel }) => {
.attr("font-size", "0.15rem")
.attr("fill", "#7C7C7C");

// Defines the lines connecting inner and outer labels
const leaderArc = d3
.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.8);

// Adds and controls how the lines are generated and positions them between inner and outer label
// Update leader lines for adjusted labels
svgGroup
.selectAll(".leader-line")
.data(pie(data))
.enter()
.append("polyline")
.attr("class", "leader-line")
.attr("points", function (d: any): any {
const posA = leaderArc.centroid(d);
const posB = outerArc.centroid(d);
const posC = outerArc.centroid(d);
const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
posC[0] = radius * 1.1 * (midAngle < Math.PI ? 1 : -1);
.attr("points", function (d: any, i): any {
const posA = labelArc.centroid(d).map((coord) => coord * 1.25);
const posB = outerArc.centroid(d).map((coord) => coord * 0.85);
const posC = [
outerLabelsData[i].x * 0.85,
outerLabelsData[i].y * 0.85,
];
return [posA, posB, posC];
})
.style("fill", "none")
Expand Down
Loading

0 comments on commit f8d9a13

Please sign in to comment.