Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(DateTimeRange): support DateTime mode #2974

Merged
merged 12 commits into from
Feb 22, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

<h4>@Localizer["Description"]</h4>

<DemoBlock Title="@Localizer["DateTimePickerTitle"]"
Introduction="@Localizer["DateTimePickerIntro"]"
Name="DateTimePicker">
<DemoBlock Title="@Localizer["DateTimePickerTitle"]" Introduction="@Localizer["DateTimePickerIntro"]" Name="DateTimePicker">
<DateTimePicker ViewMode="DatePickerViewMode.DateTime"
Value="@DateTimePickerValue" OnValueChanged="@TimePickerValueChanged">
<TimePickerSetting ShowClockScale="true" IsAutoSwitch="false" />
Expand Down
8 changes: 4 additions & 4 deletions src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
<span class="bb-time-text me-5" @onclick="SwitchView">@CurrentDateString</span>
}
<span class="bb-time-text hour" @onclick="() => SetMode(TimeMode.Hour)">@HourValue</span>
<span>:</span>
@if(ShowMinute)
<span class="bb-time-colon">:</span>
@if (ShowMinute)
{
<span class="bb-time-text minute" @onclick="() => SetMode(TimeMode.Minute)">@Value.Minutes.ToString("D2")</span>
}
else
{
<span class="bb-time-text minute">@Value.Minutes.ToString("D2")</span>
}
<span>:</span>
@if(ShowMinute && ShowSecond)
<span class="bb-time-colon">:</span>
@if (ShowMinute && ShowSecond)
{
<span class="bb-time-text second" @onclick="() => SetMode(TimeMode.Second)">@Value.Seconds.ToString("D2")</span>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
font-weight: 500;
text-align: center;
user-select: none;
display: flex;
justify-content: center;

.bb-time-text {
cursor: pointer;
Expand All @@ -68,6 +70,10 @@
display: inline-block;
cursor: pointer;
}

.bb-time-colon {
padding: 0 .25rem;
}
}

.bb-time-body {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,9 +435,9 @@ protected override void OnParametersSet()
YearText ??= Localizer[nameof(YearText)];
MonthText ??= Localizer[nameof(MonthText)];
YearPeriodText ??= Localizer[nameof(YearPeriodText)];
MonthLists = Localizer[nameof(MonthLists)].Value.Split(',').ToList();
Months = Localizer[nameof(Months)].Value.Split(',').ToList();
WeekLists = Localizer[nameof(WeekLists)].Value.Split(',').ToList();
MonthLists = [.. Localizer[nameof(MonthLists)].Value.Split(',')];
Months = [.. Localizer[nameof(Months)].Value.Split(',')];
WeekLists = [.. Localizer[nameof(WeekLists)].Value.Split(',')];

Today ??= Localizer[nameof(Today)];
Yesterday ??= Localizer[nameof(Yesterday)];
Expand Down Expand Up @@ -532,6 +532,7 @@ private async Task OnClickDateTime(DateTime d)
}
}

[ExcludeFromCodeCoverage]
private async Task OnTimeChanged(TimeSpan time)
{
CurrentTime = time;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,9 @@ protected override void OnParametersSet()
{
SelectedValue = v1.DateTime;
}
else if (Value is DateTime v2)
else
{
SelectedValue = v2;
SelectedValue = (DateTime)(object)Value;
}

if (MinValueToEmpty(SelectedValue))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,6 @@ protected override void OnParametersSet()
{
base.OnParametersSet();

// TODO: 临时禁用 DateTime 模式
if (ViewMode == DatePickerViewMode.DateTime)
{
ViewMode = DatePickerViewMode.Date;
}

StartPlaceHolderText ??= Localizer[nameof(StartPlaceHolderText)];
EndPlaceHolderText ??= Localizer[nameof(EndPlaceHolderText)];
SeparateText ??= Localizer[nameof(SeparateText)];
Expand Down Expand Up @@ -355,7 +349,7 @@ private async Task ClickTodayButton()
SelectedValue.End = GetEndDateTime(DateTime.Today);

EndValue = SelectedValue.End;
StartValue = GetSafeStartValue();
StartValue = SelectedValue.Start.GetSafeMonthDateTime(-1);
await ClickConfirmButton();
}

Expand Down Expand Up @@ -439,6 +433,4 @@ private void UpdateValue(DateTime d)
public override bool IsComplexValue(object? propertyValue) => false;

private static DateTime GetEndDateTime(DateTime dt) => dt.Date.AddHours(23).AddMinutes(59).AddSeconds(59);

private DateTime GetSafeStartValue() => SelectedValue.Start.Date == SelectedValue.End.Date ? SelectedValue.Start.GetSafeMonthDateTime(-1) : SelectedValue.Start.Date;
}
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,6 @@ async Task AddDynamicObjectExcelModelAsync()
if (DynamicContext != null)
{
// 数据源为 DataTable 新建后重建行与列
// TODO: 新建行在数据源 DataTable 中
await DynamicContext.AddAsync(SelectedRows.OfType<IDynamicObject>());
ResetDynamicContext();
SelectedRows.Clear();
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/Utils/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public static class Utility
/// <param name="cultureName">cultureName 未空时使用 CultureInfo.CurrentUICulture.Name</param>
/// <param name="forceLoad">默认 false 使用缓存值 设置 true 时内部强制重新加载</param>
/// <returns></returns>
public static IEnumerable<LocalizedString> GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false) => CacheManager.GetJsonStringByTypeName(option, assembly, typeName, cultureName, forceLoad) ?? Enumerable.Empty<LocalizedString>();
public static IEnumerable<LocalizedString> GetJsonStringByTypeName(JsonLocalizationOptions option, Assembly assembly, string typeName, string? cultureName = null, bool forceLoad = false) => CacheManager.GetJsonStringByTypeName(option, assembly, typeName, cultureName, forceLoad) ?? [];

/// <summary>
/// 通过指定程序集与类型获得 IStringLocalizer 实例
Expand Down
2 changes: 1 addition & 1 deletion test/UnitTest/Components/DateTimePickerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,7 @@ public void FormatValueAsString_Ok()

var picker = cut.Instance;
var mi = picker.GetType().GetMethod("FormatValueAsString", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!;
var v = mi.Invoke(picker, new object[] { DateTime.MinValue });
var v = mi.Invoke(picker, [DateTime.MinValue]);
Assert.Equal($"{DateTime.Today:yyyy-MM-dd}", v);
}

Expand Down
93 changes: 61 additions & 32 deletions test/UnitTest/Components/DateTimeRangeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,16 @@ await cut.InvokeAsync(() =>
[Fact]
public void OnTimeChanged_Ok()
{
// TODO: 未实现
//var cut = Context.RenderComponent<DateTimeRange>(builder =>
//{
// builder.Add(a => a.ViewMode, DatePickerViewMode.DateTime);
//});
var cut = Context.RenderComponent<DateTimeRange>(builder =>
{
builder.Add(a => a.ViewMode, DatePickerViewMode.DateTime);
});

//var panel = cut.FindComponent<TimePickerPanel>();
//cut.InvokeAsync(() => panel.Instance.SetTime(0, 0, 0));
var panel = cut.FindComponent<ClockPicker>();
cut.InvokeAsync(() => panel.Instance.SetTime(0, 0, 0));

//var body = cut.FindComponent<DatePickerBody>();
//Assert.Equal(TimeSpan.Zero, body.Instance.Value.TimeOfDay);
var body = cut.FindComponent<DatePickerBody>();
Assert.Equal(TimeSpan.Zero, body.Instance.Value.TimeOfDay);
}

[Fact]
Expand Down Expand Up @@ -193,7 +192,7 @@ public void SidebarItems_Ok()
}

[Fact]
public void OnConfirm_Ok()
public async Task OnConfirm_Ok()
{
var value = false;
var cut = Context.RenderComponent<DateTimeRange>(pb =>
Expand All @@ -209,7 +208,7 @@ public void OnConfirm_Ok()
});
});

cut.InvokeAsync(() =>
await cut.InvokeAsync(() =>
{
// 选择开始未选择结束
cut.Find(".cell").Click();
Expand All @@ -229,27 +228,38 @@ public void OnConfirm_Ok()
Assert.True(DateTime.TryParseExact(input.GetAttribute("Value"), "MM/dd/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out var _));

// datetime
//cut.SetParametersAndRender(pb =>
//{
// pb.Add(a => a.ViewMode, DatePickerViewMode.DateTime);
// pb.Add(a => a.DateTimeFormat, "MM/dd/yyyy HH:mm:ss");
//});
//cut.InvokeAsync(() =>
//{
// // 选择开始未选择结束
// cut.Find(".cell").Click();
// var cells = cut.FindAll(".is-confirm");
// cells.First(s => s.TextContent == "确定").Click();

// // 选择时间大于当前时间
// cells = cut.FindAll(".date-table .cell");
// cells[cells.Count - 1].Click();
// cells = cut.FindAll(".is-confirm");
// cells.First(s => s.TextContent == "确定").Click();
//});
//input = cut.Find(".datetime-range-input");
//Assert.True(input.ClassList.Contains("datetime"));
//Assert.True(DateTime.TryParseExact(input.GetAttribute("Value"), "MM/dd/yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var _));
cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.ViewMode, DatePickerViewMode.DateTime);
pb.Add(a => a.DateTimeFormat, "MM/dd/yyyy HH:mm:ss");
});
await cut.InvokeAsync(() =>
{
// 选择开始未选择结束
cut.Find(".cell").Click();
var cells = cut.FindAll(".is-confirm");
cells.First(s => s.TextContent == "确定").Click();

// 选择时间大于当前时间
cells = cut.FindAll(".date-table .cell");
cells[cells.Count - 1].Click();
cells = cut.FindAll(".is-confirm");
cells.First(s => s.TextContent == "确定").Click();
});
input = cut.Find(".datetime-range-input");
Assert.True(input.ClassList.Contains("datetime"));
Assert.True(DateTime.TryParseExact(input.GetAttribute("Value"), "MM/dd/yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var _));

// timeformat
cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.TimeFormat, "hhmmss");
});
var labels = cut.FindAll(".picker-panel-header-label");
Assert.Equal(6, labels.Count);

var timeLabel = labels[2];
timeLabel.MarkupMatches("<span role=\"button\" class=\"picker-panel-header-label\" diff:ignore>00000</span>");
}

[Fact]
Expand Down Expand Up @@ -510,4 +520,23 @@ private class Dummy
[Required]
public DateTimeRangeValue? Value { get; set; }
}

[Fact]
public async Task GetSafeStartValue_Ok()
{
var cut = Context.RenderComponent<DateTimeRange>(builder =>
{
builder.Add(a => a.Value, new DateTimeRangeValue());
builder.Add(a => a.ShowToday, true);
builder.Add(a => a.ShowClearButton, false);
});
var button = cut.Find(".picker-panel-link-btn.is-confirm");
await cut.InvokeAsync(() =>
{
button.Click();
});

Assert.Equal(DateTime.Today, cut.Instance.Value.Start);
Assert.Equal(DateTime.Today.AddDays(1).AddSeconds(-1), cut.Instance.Value.End);
}
}
33 changes: 16 additions & 17 deletions test/UnitTest/Components/TimePickerSettingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,22 @@ public void PickerSetting_Ok()
[Fact]
public void RangeSetting_Ok()
{
// TODO: 未实现
//var cut = Context.RenderComponent<DateTimeRange>(pb =>
//{
// pb.Add(a => a.ViewMode, DatePickerViewMode.DateTime);
// pb.AddChildContent<TimePickerSetting>(pb =>
// {
// pb.Add(a => a.ShowMinute, false);
// pb.Add(a => a.ShowSecond, false);
// pb.Add(a => a.ShowClockScale, true);
// pb.Add(a => a.IsAutoSwitch, false);
// });
//});
var cut = Context.RenderComponent<DateTimeRange>(pb =>
{
pb.Add(a => a.ViewMode, DatePickerViewMode.DateTime);
pb.AddChildContent<TimePickerSetting>(pb =>
{
pb.Add(a => a.ShowMinute, false);
pb.Add(a => a.ShowSecond, false);
pb.Add(a => a.ShowClockScale, true);
pb.Add(a => a.IsAutoSwitch, false);
});
});

//var picker = cut.FindComponent<TimePickerPanel>();
//Assert.False(picker.Instance.ShowMinute);
//Assert.False(picker.Instance.ShowSecond);
//Assert.False(picker.Instance.IsAutoSwitch);
//Assert.True(picker.Instance.ShowClockScale);
var picker = cut.FindComponent<ClockPicker>();
Assert.False(picker.Instance.ShowMinute);
Assert.False(picker.Instance.ShowSecond);
Assert.False(picker.Instance.IsAutoSwitch);
Assert.True(picker.Instance.ShowClockScale);
}
}
2 changes: 1 addition & 1 deletion test/UnitTest/Components/ValidateFormTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ public void ValidateFieldAsync_Ok()

var context = new ValidationContext(new Foo());
var result = new List<ValidationResult>();
method.Invoke(form, new object[] { context, result });
method.Invoke(form, [context, result]);
}

private class HasServiceAttribute : ValidationAttribute
Expand Down