diff --git a/src/Blashing.Core.Tests/GraphWidgetTest.cs b/src/Blashing.Core.Tests/GraphWidgetTest.cs
index 82bba73..3f541f7 100644
--- a/src/Blashing.Core.Tests/GraphWidgetTest.cs
+++ b/src/Blashing.Core.Tests/GraphWidgetTest.cs
@@ -7,7 +7,7 @@ namespace Blashing.Core.Tests;
public class GraphWidgetTest : BunitContext
{
[Fact]
- public void CommentsWidgetMarkupShouldContainPassedInValues()
+ public void GraphWidgetMarkupShouldContainPassedInValues()
{
var title = "Graph 1";
var moreInfo = "This graph contains lots of useful data";
@@ -17,11 +17,6 @@ public void CommentsWidgetMarkupShouldContainPassedInValues()
.Add(p => p.MoreInfo, moreInfo)
);
- //var widget = @"
- //
@Title
- //
- // @MoreInfo
";
-
var expectedTitleMarkup = $"{title}
";
cut.FindAll("h1")[0].MarkupMatches(expectedTitleMarkup);
@@ -30,7 +25,7 @@ public void CommentsWidgetMarkupShouldContainPassedInValues()
}
[Fact]
- public void CommentsWidgetShouldContainPassedInValues()
+ public void GraphWidgetShouldContainPassedInValues()
{
var title = "Graph 1";
var moreInfo = "This graph contains lots of useful data";
@@ -44,4 +39,81 @@ public void CommentsWidgetShouldContainPassedInValues()
Assert.Equal(graphWidget.Title, title);
Assert.Equal(graphWidget.MoreInfo, moreInfo);
}
+
+ [Fact]
+ public void GraphWidgetWithCurrentValueShouldRenderValue()
+ {
+ var title = "Graph 1";
+ var current = "42";
+ var prefix = "$";
+ var suffix = "k";
+
+ var cut = Render(parameters => parameters
+ .Add(p => p.Title, title)
+ .Add(p => p.Current, current)
+ .Add(p => p.Prefix, prefix)
+ .Add(p => p.Suffix, suffix)
+ );
+
+ var graphWidget = cut.Instance;
+ Assert.Equal(graphWidget.Current, current);
+ Assert.Equal(graphWidget.Prefix, prefix);
+ Assert.Equal(graphWidget.Suffix, suffix);
+
+ var h2 = cut.FindAll("h2")[0];
+ h2.MarkupMatches($"{prefix}{current}{suffix}
");
+ }
+
+ [Fact]
+ public void GraphWidgetWithPointsShouldRenderSvg()
+ {
+ var points = new List { 10, 20, 15, 30, 25 };
+
+ var cut = Render(parameters => parameters
+ .Add(p => p.Title, "Graph")
+ .Add(p => p.Points, points)
+ );
+
+ var graphWidget = cut.Instance;
+ Assert.Equal(graphWidget.Points, points);
+
+ var svgs = cut.FindAll("svg");
+ Assert.Single(svgs);
+ }
+
+ [Fact]
+ public void GraphWidgetWithoutPointsShouldNotRenderSvg()
+ {
+ var cut = Render(parameters => parameters
+ .Add(p => p.Title, "Graph")
+ );
+
+ var svgs = cut.FindAll("svg");
+ Assert.Empty(svgs);
+ }
+
+ [Fact]
+ public void GenerateSvgPathShouldReturnEmptyForFewerThanTwoPoints()
+ {
+ var cut = Render(parameters => parameters
+ .Add(p => p.Points, new List { 42 })
+ );
+
+ var path = cut.Instance.GenerateSvgPath();
+ Assert.Equal(string.Empty, path);
+ }
+
+ [Fact]
+ public void GenerateSvgPathShouldReturnValidPathForTwoPoints()
+ {
+ var cut = Render(parameters => parameters
+ .Add(p => p.Points, new List { 10, 20 })
+ );
+
+ var path = cut.Instance.GenerateSvgPath();
+ Assert.NotEmpty(path);
+ Assert.StartsWith("M ", path);
+ Assert.Contains(" C ", path);
+ Assert.EndsWith(" Z", path);
+ }
}
\ No newline at end of file
diff --git a/src/Blashing.Core/Components/Graph/GraphWidget.razor b/src/Blashing.Core/Components/Graph/GraphWidget.razor
index fa9c17a..9af39fa 100644
--- a/src/Blashing.Core/Components/Graph/GraphWidget.razor
+++ b/src/Blashing.Core/Components/Graph/GraphWidget.razor
@@ -1,9 +1,20 @@
@inherits BaseWidget;
diff --git a/src/Blashing.Core/Components/Graph/GraphWidget.razor.cs b/src/Blashing.Core/Components/Graph/GraphWidget.razor.cs
index 4765dce..5d7b397 100644
--- a/src/Blashing.Core/Components/Graph/GraphWidget.razor.cs
+++ b/src/Blashing.Core/Components/Graph/GraphWidget.razor.cs
@@ -1,3 +1,4 @@
+using System.Text;
using Microsoft.AspNetCore.Components;
namespace Blashing.Core.Components.Graph;
@@ -7,11 +8,62 @@ public partial class GraphWidget : BaseWidget
[Parameter]
public string? Title { get; set; }
+ [Parameter]
+ public string? Current { get; set; }
+
+ [Parameter]
+ public string? Prefix { get; set; }
+
+ [Parameter]
+ public string? Suffix { get; set; }
+
[Parameter]
public string? MoreInfo { get; set; }
-
+
+ [Parameter]
+ public IEnumerable Points { get; set; } = [];
+
protected override void OnParametersSet()
{
- BackgroundColor ??= "#ff9618";
+ // #dc5945 matches the $background-color in GraphWidget.razor.scss
+ BackgroundColor ??= "#dc5945";
+ }
+
+ private const int SvgViewBoxWidth = 300;
+ private const int SvgViewBoxHeight = 200;
+ private const double SvgTopPadding = 10;
+
+ public string GenerateSvgPath()
+ {
+ var pointsList = Points?.ToList();
+ if (pointsList == null || pointsList.Count < 2)
+ return string.Empty;
+
+ var min = pointsList.Min();
+ var max = pointsList.Max();
+ var range = max - min;
+ if (range == 0) range = 1;
+
+ var n = pointsList.Count;
+ var coords = pointsList.Select((y, i) => (
+ x: (double)i / (n - 1) * SvgViewBoxWidth,
+ y: (SvgViewBoxHeight - SvgTopPadding) - ((y - min) / range) * (SvgViewBoxHeight - SvgTopPadding)
+ )).ToList();
+
+ var sb = new StringBuilder();
+ sb.Append(FormattableString.Invariant($"M {coords[0].x:F1} {coords[0].y:F1}"));
+
+ for (int i = 0; i < coords.Count - 1; i++)
+ {
+ double midX = (coords[i].x + coords[i + 1].x) / 2;
+ sb.Append(FormattableString.Invariant(
+ $" C {midX:F1} {coords[i].y:F1}, {midX:F1} {coords[i + 1].y:F1}, {coords[i + 1].x:F1} {coords[i + 1].y:F1}"));
+ }
+
+ sb.Append(FormattableString.Invariant($" L {coords[^1].x:F1} {SvgViewBoxHeight}.0"));
+ sb.Append(FormattableString.Invariant($" L {coords[0].x:F1} {SvgViewBoxHeight}.0"));
+ sb.Append(" Z");
+
+ return sb.ToString();
}
}
\ No newline at end of file
diff --git a/src/Blashing.Core/Components/Graph/GraphWidget.razor.css b/src/Blashing.Core/Components/Graph/GraphWidget.razor.css
index 6b8c3a6..e60bafb 100644
--- a/src/Blashing.Core/Components/Graph/GraphWidget.razor.css
+++ b/src/Blashing.Core/Components/Graph/GraphWidget.razor.css
@@ -20,6 +20,8 @@ svg {
fill-opacity: 0.4;
left: 0px;
top: 0px;
+ width: 100%;
+ height: 100%;
}
.title, .value {
diff --git a/src/Blashing.Core/Components/Graph/GraphWidget.razor.scss b/src/Blashing.Core/Components/Graph/GraphWidget.razor.scss
index 099d5af..1598bbb 100644
--- a/src/Blashing.Core/Components/Graph/GraphWidget.razor.scss
+++ b/src/Blashing.Core/Components/Graph/GraphWidget.razor.scss
@@ -22,6 +22,8 @@ $tick-color: rgba(0, 0, 0, 0.4);
fill-opacity: 0.4;
left: 0px;
top: 0px;
+ width: 100%;
+ height: 100%;
}
.title, .value {
diff --git a/src/Blashing.Shared/Pages/Demo.razor b/src/Blashing.Shared/Pages/Demo.razor
index e251578..7007abb 100644
--- a/src/Blashing.Shared/Pages/Demo.razor
+++ b/src/Blashing.Shared/Pages/Demo.razor
@@ -34,7 +34,7 @@
@**@
-
+
@@ -45,4 +45,5 @@
@code {
List<(string label, string value)>? Items = new() { ("Turn-key", "0"), ("Synergy", "4"), ("Exit strategy", "11"), ("Paridigm shift", "3"), ("Enterprise", "20"), ("Pivoting", "7"), ("Web 2.0", "16"), ("Leverage", "25"), ("Streamlininess", "9") };
+ List GraphPoints = new() { 10, 30, 25, 50, 45, 70, 60, 80, 65 };
}
\ No newline at end of file
diff --git a/src/Blashing.Shared/Pages/Index.razor b/src/Blashing.Shared/Pages/Index.razor
index 90d34d8..f783d9a 100644
--- a/src/Blashing.Shared/Pages/Index.razor
+++ b/src/Blashing.Shared/Pages/Index.razor
@@ -16,7 +16,7 @@
-
+
@@ -42,4 +42,5 @@
@code {
List<(string label, string value)>? Items = new() { ("a", "b"), ("c", "d") };
+ List GraphPoints = new() { 10, 30, 25, 50, 45, 70, 60, 80, 65 };
}
\ No newline at end of file
diff --git a/src/Blashing.Shared/wwwroot/images/graph-widget.png b/src/Blashing.Shared/wwwroot/images/graph-widget.png
new file mode 100644
index 0000000..b174676
Binary files /dev/null and b/src/Blashing.Shared/wwwroot/images/graph-widget.png differ
diff --git a/src/Blashing.Stories/Stories/GraphWidget.stories.razor b/src/Blashing.Stories/Stories/GraphWidget.stories.razor
index 0dcb1e2..62a3331 100644
--- a/src/Blashing.Stories/Stories/GraphWidget.stories.razor
+++ b/src/Blashing.Stories/Stories/GraphWidget.stories.razor
@@ -5,19 +5,23 @@
+
+
+
-
-
+
+
+
-
+
- @* *@