From 703d653ec5e11dd46d6fffad6e13dbdef669ae7c Mon Sep 17 00:00:00 2001 From: Jonathan Sheely Date: Wed, 31 Jan 2024 11:37:05 -0500 Subject: [PATCH] Adds ValueFormatter to ProgressBar --- docs/input/widgets/breakdownchart.md | 10 ++++ .../Extensions/BarChartExtensions.cs | 37 ++++++++++++++ .../Widgets/Charts/BarChart.cs | 6 +++ src/Spectre.Console/Widgets/ProgressBar.cs | 3 +- .../ProgressBar/Formatted.Output.verified.txt | 1 + .../ProgressBar/Render.Output.verified.txt | 1 + .../Unit/Widgets/ProgressBarTests.cs | 51 +++++++++++++++++++ 7 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Formatted.Output.verified.txt create mode 100644 test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Render.Output.verified.txt create mode 100644 test/Spectre.Console.Tests/Unit/Widgets/ProgressBarTests.cs diff --git a/docs/input/widgets/breakdownchart.md b/docs/input/widgets/breakdownchart.md index f00ec0f..a30fd54 100644 --- a/docs/input/widgets/breakdownchart.md +++ b/docs/input/widgets/breakdownchart.md @@ -132,3 +132,13 @@ AnsiConsole.Write(new BreakdownChart() .AddItem(new Fruit("Mango", 3, Color.Orange4)) .AddItems(items)); ``` + +### Add value formatter to chart numbers + +```csharp +var chart = new BreakdownChart(); +chart.UseValueFormater(value => value.ToString("N0")); + +// This can be simplified as extension methods are chainable. +var chart = new BreakdownChart().UseValueFormatter(v => v.ToString("N0")); +``` diff --git a/src/Spectre.Console/Extensions/BarChartExtensions.cs b/src/Spectre.Console/Extensions/BarChartExtensions.cs index 484f81b..8029981 100644 --- a/src/Spectre.Console/Extensions/BarChartExtensions.cs +++ b/src/Spectre.Console/Extensions/BarChartExtensions.cs @@ -116,6 +116,43 @@ public static class BarChartExtensions return chart; } + /// + /// Sets the value formatter for the bar chart using culture info. + /// + /// The bar chart. + /// The value formatter function with culture info. + /// The same instance so that multiple calls can be chained. + public static BarChart UseValueFormatter(this BarChart chart, Func? func) + { + if (chart is null) + { + throw new ArgumentNullException(nameof(chart)); + } + + chart.ValueFormatter = func; + return chart; + } + + /// + /// Sets the value formatter for the bar chart. + /// + /// The bar chart. + /// The value formatter to use. + /// The same instance so that multiple calls can be chained. + public static BarChart UseValueFormatter(this BarChart chart, Func? func) + { + if (chart is null) + { + throw new ArgumentNullException(nameof(chart)); + } + + chart.ValueFormatter = func != null + ? (value, _) => func(value) + : null; + + return chart; + } + /// /// Sets the width of the bar chart. /// diff --git a/src/Spectre.Console/Widgets/Charts/BarChart.cs b/src/Spectre.Console/Widgets/Charts/BarChart.cs index b6367b7..c09a997 100644 --- a/src/Spectre.Console/Widgets/Charts/BarChart.cs +++ b/src/Spectre.Console/Widgets/Charts/BarChart.cs @@ -43,6 +43,11 @@ public sealed class BarChart : Renderable, IHasCulture /// Defaults to null, which corresponds to largest value in chart. public double? MaxValue { get; set; } + /// + /// Gets or sets the function used to format the values of the bar chart. + /// + public Func? ValueFormatter { get; set; } + /// /// Initializes a new instance of the class. /// @@ -90,6 +95,7 @@ public sealed class BarChart : Renderable, IHasCulture AsciiBar = '█', ShowValue = ShowValues, Culture = Culture, + ValueFormatter = ValueFormatter, }); } diff --git a/src/Spectre.Console/Widgets/ProgressBar.cs b/src/Spectre.Console/Widgets/ProgressBar.cs index 2e6ea54..3587eb5 100644 --- a/src/Spectre.Console/Widgets/ProgressBar.cs +++ b/src/Spectre.Console/Widgets/ProgressBar.cs @@ -15,6 +15,7 @@ internal sealed class ProgressBar : Renderable, IHasCulture public bool ShowValue { get; set; } public bool IsIndeterminate { get; set; } public CultureInfo? Culture { get; set; } + public Func? ValueFormatter { get; set; } public Style CompletedStyle { get; set; } = Color.Yellow; public Style FinishedStyle { get; set; } = Color.Green; @@ -50,7 +51,7 @@ internal sealed class ProgressBar : Renderable, IHasCulture var barCount = Math.Max(0, (int)(width * (completedBarCount / MaxValue))); // Show value? - var value = completedBarCount.ToString(Culture ?? CultureInfo.InvariantCulture); + var value = ValueFormatter != null ? ValueFormatter(completedBarCount, Culture ?? CultureInfo.InvariantCulture) : completedBarCount.ToString(Culture ?? CultureInfo.InvariantCulture); if (ShowValue) { barCount = barCount - value.Length - 1; diff --git a/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Formatted.Output.verified.txt b/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Formatted.Output.verified.txt new file mode 100644 index 0000000..de94de7 --- /dev/null +++ b/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Formatted.Output.verified.txt @@ -0,0 +1 @@ +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9,000 diff --git a/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Render.Output.verified.txt b/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Render.Output.verified.txt new file mode 100644 index 0000000..d572628 --- /dev/null +++ b/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Render.Output.verified.txt @@ -0,0 +1 @@ +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9000 \ No newline at end of file diff --git a/test/Spectre.Console.Tests/Unit/Widgets/ProgressBarTests.cs b/test/Spectre.Console.Tests/Unit/Widgets/ProgressBarTests.cs new file mode 100644 index 0000000..7decab6 --- /dev/null +++ b/test/Spectre.Console.Tests/Unit/Widgets/ProgressBarTests.cs @@ -0,0 +1,51 @@ +namespace Spectre.Console.Tests.Unit; + +[UsesVerify] +[ExpectationPath("Widgets/ProgressBar")] +public class ProgressBarTests +{ + [Fact] + [Expectation("Render")] + public async Task Should_Render_Correctly() + { + // Given + var console = new TestConsole(); + + var bar = new ProgressBar() + { + Width = 60, + Value = 9000, + MaxValue = 9000, + ShowValue = true, + }; + + // When + console.Write(bar); + + // Then + await Verifier.Verify(console.Output); + } + + [Fact] + [Expectation("Formatted")] + public async Task Should_Render_ValueFormatted() + { + // Given + var console = new TestConsole(); + + var bar = new ProgressBar() + { + Width = 60, + Value = 9000, + MaxValue = 9000, + ShowValue = true, + ValueFormatter = (value, _) => value.ToString("N0"), + }; + + // When + console.Write(bar); + + // Then + await Verifier.Verify(console.Output); + } +} \ No newline at end of file