@page "/ChartClippingRepro"
@using Syncfusion.Blazor.Buttons
@using Syncfusion.Blazor.DropDowns
@using Syncfusion.Blazor.Calendars
@using Syncfusion.Blazor.Charts
@using Syncfusion.Blazor.Spinner
<style>
.test-report-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 24px;
}
.test-chart .e-chart text {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.e-btn {
padding: 0 24px;
font-size: 1.1rem;
min-width: 180px;
}
.test-title {
text-align: center;
width: 100%;
margin: 16px 0;
}
.test-controls-row {
display: flex;
flex-direction: row;
gap: 18px;
background: #f6fafd;
border-radius: 8px;
padding: 16px 18px;
/* Allows wrapping on small screens */
flex-wrap: wrap;
}
.test-controls-row label {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.375rem;
color: #101010;
}
.test-controls {
display: flex;
flex-direction: column;
min-width: 180px;
flex: 1 1 0;
min-height: 75px; /* Reserve enough space for label, input, and validation */
}
.test-controls-actions {
display: flex;
flex-direction: row; /* Ensure buttons are in a row */
gap: 12px;
align-items: center;
flex-wrap: wrap;
margin-top: 5px; /*To align the buttons vertically*/
/* min-height: 75px; */
}
.test-chart {
}
@@media (max-width: 900px) {
.test-controls{
min-height: 0; /* Remove the forced height */
}
.test-controls-actions {
flex-direction: column; /* Stack buttons vertically */
width: 100%;
align-items: stretch; /* Make children (buttons) stretch to full width */
}
}
</style>
<PageTitle>TestApp</PageTitle>
<h4 class="test-title">Chart Clipping Repro</h4>
<div class="test-report-container">
@if (EditContext != null)
{
<EditForm EditContext="@EditContext">
<DataAnnotationsValidator />
<div class="test-controls-row">
<div class="test-controls">
<label>Date</label>
<SfDatePicker TValue="DateTime?"
@bind-Value=@InputModel.Date
Min=@MinDate
Max=@MaxDate
AllowEdit="false"
ShowClearButton="true"/>
<ValidationMessage For="@(() => InputModel.Date)" />
</div>
<div class="test-controls">
<label>Region</label>
<SfDropDownList TValue="string"
TItem="string"
DataSource="@Regions"
@bind-Value="InputModel.Region"
Placeholder="Select region" />
<ValidationMessage For="@(() => InputModel.Region)" />
</div>
<div class="test-controls-actions">
<SfButton IsPrimary="true" OnClick="OnRefreshAsync" Disabled="@IsBusy" type="button">
@if (IsBusy && InputModel.Action == MyPageAction.Refresh)
{
<span class="spinner-border spinner-border-sm" role="status" aria-label="Refreshing"></span>
<span>Refreshing...</span>
}
else
{
<span>Refresh</span>
}
</SfButton>
</div>
</div>
</EditForm>
}
else
{
<div>Loading controls...</div>
}
@if (MyForecasts.Count > 0)
{
<div class="test-chart">
<SfChart Title="@ChartTitle1" Width="100%" Height="400px">
<ChartTitleStyle TextOverflow="Syncfusion.Blazor.Charts.TextOverflow.Wrap"></ChartTitleStyle>
<ChartArea>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartMargin Right="10" />
<ChartEvents OnAxisLabelRender="ChartAxisLabelRender"
OnDataLabelRender="ChartDataLabelRender"
SharedTooltipRender="ChartSharedTooltipRender">
</ChartEvents>
<ChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.Double"
IntervalType="Syncfusion.Blazor.Charts.IntervalType.Auto"
Title="Hour Ending"
Interval="1" />
<ChartPrimaryYAxis Title="MWs" LabelFormat="N0">
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<!-- Stacked Area Series (order: bottom to top) -->
<ChartSeries DataSource="@MyForecasts"
XName="HourEnding"
YName="Wind"
Name="Wind"
Type="Syncfusion.Blazor.Charts.ChartSeriesType.StackingArea"
Fill="#00B050"
Opacity="0.7" />
<ChartSeries DataSource="@MyForecasts"
XName="HourEnding"
YName="Solr"
Name="Solr"
Type="Syncfusion.Blazor.Charts.ChartSeriesType.StackingArea"
Fill="#FFD600"
Opacity="0.7" />
</ChartSeriesCollection>
<ChartLegendSettings Visible="true" />
<ChartTooltipSettings Enable="true" Shared="true" />
</SfChart>
</div>
<div class="test-chart">
<SfChart Title="@ChartTitle2" Width="100%" Height="400px">
<ChartTitleStyle TextOverflow="Syncfusion.Blazor.Charts.TextOverflow.Wrap"></ChartTitleStyle>
<ChartArea>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartMargin Right="10" />
<ChartEvents OnAxisLabelRender="ChartAxisLabelRender"
OnDataLabelRender="ChartDataLabelRender"
SharedTooltipRender="ChartSharedTooltipRender">
</ChartEvents>
<ChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.Double"
IntervalType="Syncfusion.Blazor.Charts.IntervalType.Auto"
Title="Hour Ending"
Interval="1" />
<ChartPrimaryYAxis Title="MWs" LabelFormat="N0">
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@MyForecasts"
XName="HourEnding"
YName="Solr"
Name="Solr"
Type="Syncfusion.Blazor.Charts.ChartSeriesType.Line"
Width="2"
Fill="#FF4070"
DashArray="5,2" />
<ChartSeries DataSource="@MyForecasts"
XName="HourEnding"
YName="Wind"
Name="Wind"
Type="Syncfusion.Blazor.Charts.ChartSeriesType.Line"
Width="2"
Fill="#A3D233"
DashArray="5,2" />
</ChartSeriesCollection>
<ChartLegendSettings Visible="true" />
<ChartTooltipSettings Enable="true" Shared="true" />
</SfChart>
</div>
}
else if (IsBusy)
{
<div>Loading charts...</div>
}
else
{
<div>No chart data available.</div>
}
<SfSpinner Visible="@IsBusy"></SfSpinner>
</div>
@code {
List<MyForecast> MyForecasts = new();
MyPageInputModel InputModel = new();
EditContext? EditContext;
bool IsBusy;
// Dates
DateTime MinDate = DateTime.Today;
DateTime MaxDate = DateTime.Today.AddDays(1);
// Chart data
string ChartTitle1 = string.Empty;
string ChartTitle2 = string.Empty;
// Regions
List<string> Regions = new() { "Reg 1", "Reg 2" };
// --- Lifecycle Methods ---
protected override async Task OnInitializedAsync()
{
EditContext = new EditContext(InputModel);
await LoadPageDataAsync();
UpdateChartTitle();
}
// --- Event Handlers ---
async Task OnRefreshAsync()
{
InputModel.Action = MyPageAction.Refresh;
await LoadPageDataAsync();
UpdateChartTitle();
InputModel.Action = MyPageAction.None;
}
async Task LoadPageDataAsync()
{
IsBusy = true;
// Simulate an API call
await Task.Delay(50);
MyForecasts = new List<MyForecast>
{
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 1, Region = "Reg 1", Wind = 8927, Solr = 0 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 2, Region = "Reg 1", Wind = 8830, Solr = 0 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 3, Region = "Reg 1", Wind = 8839, Solr = 0 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 4, Region = "Reg 1", Wind = 8790, Solr = 0 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 5, Region = "Reg 1", Wind = 8692, Solr = 0 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 6, Region = "Reg 1", Wind = 8345, Solr = 37 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 7, Region = "Reg 1", Wind = 7938, Solr = 1404 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 8, Region = "Reg 1", Wind = 7698, Solr = 5426 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 9, Region = "Reg 1", Wind = 7697, Solr = 7903 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 10, Region = "Reg 1", Wind = 7691, Solr = 8764 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 11, Region = "Reg 1", Wind = 7582, Solr = 9194 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 12, Region = "Reg 1", Wind = 7514, Solr = 9989 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 13, Region = "Reg 1", Wind = 7674, Solr = 10195 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 14, Region = "Reg 1", Wind = 7558, Solr = 10340 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 15, Region = "Reg 1", Wind = 7586, Solr = 10167 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 16, Region = "Reg 1", Wind = 7400, Solr = 9713 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 17, Region = "Reg 1", Wind = 7174, Solr = 8407 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 18, Region = "Reg 1", Wind = 7392, Solr = 4721 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 19, Region = "Reg 1", Wind = 7115, Solr = 932 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 20, Region = "Reg 1", Wind = 7292, Solr = 3 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 21, Region = "Reg 1", Wind = 7182, Solr = 0 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 22, Region = "Reg 1", Wind = 7096, Solr = 0 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 23, Region = "Reg 1", Wind = 7221, Solr = 0 },
new() { Date = DateTime.Parse("4/14/2026"), HourEnding = 24, Region = "Reg 1", Wind = 8890, Solr = 0 },
};
IsBusy = false;
}
void UpdateChartTitle()
{
if (InputModel.Date.HasValue)
{
ChartTitle1 = $"Chart 1 on {InputModel.Date: ddd, MM/dd/yyyy}";
ChartTitle2 = $"Chart 2 on {InputModel.Date: ddd, MM/dd/yyyy}";
}
}
// Reference: https://blazor.syncfusion.com/documentation/chart/axis-labels#label-customization-using-event
void ChartAxisLabelRender(Syncfusion.Blazor.Charts.AxisLabelRenderEventArgs args)
{
//args.LabelStyle.Color = "Black";
if (args.Axis.Name == "PrimaryXAxis")
{
// Format X axis label here
}
else if (args.Axis.Name == "PrimaryYAxis")
{
if (double.TryParse(args.Text.Replace(",", ""), out double value))
{
if (Math.Abs(args.Value) >= 1000)
{
args.Text = (value / 1000).ToString("0.#") + "K";
}
}
}
}
void ChartDataLabelRender(Syncfusion.Blazor.Charts.TextRenderEventArgs args)
{
// Format the value (e.g., 12,345)
if (double.TryParse(args.Text, out var value))
{
args.Text = value.ToString("N0");
}
}
void ChartSharedTooltipRender(SharedTooltipRenderEventArgs args)
{
if (args.Data is null || args.Data.Count == 0)
return;
args.HeaderText = $"HE {args.HeaderText}";
var lines = new List<string>();
foreach (var d in args.Data)
{
if (double.TryParse(d.PointY.ToString(), out var value))
lines.Add($"{d.SeriesName}: {value:N0}");
else
lines.Add($"{d.SeriesName}: N/A");
}
args.Text = lines;
}
// --- Data Models ---
public class MyPageInputModel
{
public MyPageAction Action { get; set; } = MyPageAction.None;
public DateTime? Date { get; set; } = DateTime.Today;
public string Region { get; set; } = "Reg 1";
}
public class MyForecast
{
public DateTime Date { get; set; }
public int HourEnding { get; set; }
public string Region { get; set; } = default!;
public double? Wind { get; set; }
public double? Solr { get; set; }
}
public enum MyPageAction
{
None,
Refresh
}
}