feat(Dialog): add ShowModal extensions method (#4552)

* feat(Dialog): add ShowLiteralConfirmModal method

* test(Dialog): add unit test for ShowLiteralConfirmModal

* feat(ResizeNotification): remove ResizeNotification service (#4550)

* chore: 更新 scss 链接

* refactor: 移除服务

* refactor: 移除 ResizeNotification 组件

* refactor: 增加系列化标签

* refactor: 更新 Responsive 组件

* test: 更新单元测试

* test: 更新单元测试

* refactor: 更新代码

* test: 更新单元测试

* test: 更新单元测试

* test: 更新单元测试

* refactor: 重构 ShowModal 方法

* doc: 更新示例

* test: 更新单元测试

* test: 更新单元测试

---------

Co-authored-by: MadLongTom <36219016+MadLongTom@users.noreply.github.com>
This commit is contained in:
Argo Zhang 2024-10-26 13:50:33 +08:00 committed by GitHub
parent 3a939f288c
commit 74a74d3cd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 113 additions and 15 deletions

View File

@ -124,7 +124,8 @@ private async Task OnClick()
<li>@Localizer["ModalDialogTip5"]</li>
</ul>
</section>
<Button OnClick="@OnResultDialogClick">@Localizer["ModalDialogButton"]</Button>
<Button Text="@Localizer["ModalDialogButton"]" OnClick="@OnResultDialogClick"></Button>
<Button Text="@Localizer["ConfirmDialogButton"]" OnClick="@OnConfirmModalClick"></Button>
<ConsoleLogger @ref="ModalDialogLogger" />
</DemoBlock>

View File

@ -163,6 +163,13 @@ public sealed partial class Dialogs
ModalDialogLogger.Log($"The return value of the popup window is: {result} The return value of the component is: {DemoValue1}");
}
private async Task OnConfirmModalClick()
{
var result = await DialogService.ShowModal(Localizer["ConfirmDialogModalContent"], Localizer["ConfirmDialogModalTitle"]);
ModalDialogLogger.Log($"The return value of the popup window is: {result} no component provider");
}
private async Task OnEditDialogClick()
{
var option = new EditDialogOption<Foo>()

View File

@ -902,7 +902,10 @@
"ExportPdfDialogTitle": "Pop up window with export Pdf function",
"ExportPdfDialogIntro": "Set <code>ShowExportPdfButtonInHeader</code> to display an export PDF button on the <code>Header</code>",
"ExportPdfDialogTip": "More parameters can be set by setting <code>ExportPdfButtonOptions</code>",
"ExportPdfButton": "Export Pdf"
"ExportPdfButton": "Export Pdf",
"ConfirmDialogButton": "Popup Modal",
"ConfirmDialogModalTitle": "Literal Confirmation Modal",
"ConfirmDialogModalContent": "<p>this is the prompt message. </p><div class=\"text-danger\">this is a danger info</div>"
},
"BootstrapBlazor.Server.Components.Samples.Dispatches": {
"Title": "Dispatch message distribution",

View File

@ -902,7 +902,10 @@
"ExportPdfDialogTitle": "带导出 Pdf 功能的弹窗",
"ExportPdfDialogIntro": "通过设置 <code>ShowExportPdfButtonInHeader</code> 使 <code>Header</code> 上显示一个导出 pdf 按钮",
"ExportPdfDialogTip": "可通过设置 <code>ExportPdfButtonOptions</code> 对更多参数进行设置",
"ExportPdfButton": "导出 Pdf 弹窗"
"ExportPdfButton": "导出 Pdf 弹窗",
"ConfirmDialogButton": "弹出模态框",
"ConfirmDialogModalTitle": "文字确认模态框",
"ConfirmDialogModalContent": "<p>这是一个文字确认模态框</p><div class=\"text-danger\">这是警告信息</div>"
},
"BootstrapBlazor.Server.Components.Samples.Dispatches": {
"Title": "Dispatch 消息分发",

View File

@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
using Microsoft.AspNetCore.Components.Rendering;
namespace BootstrapBlazor.Components;
/// <summary>
@ -71,21 +73,23 @@ public static class DialogServiceExtensions
public static async Task<DialogResult> ShowModal<TDialog>(this DialogService service, ResultDialogOption option, Dialog? dialog = null)
where TDialog : IComponent, IResultDialog
{
IResultDialog? resultDialog = null;
option.GetDialog = () => resultDialog;
option.BodyTemplate = builder =>
if (option.BodyTemplate == null)
{
var index = 0;
builder.OpenComponent(index++, typeof(TDialog));
if (option.ComponentParameters != null)
IResultDialog? resultDialog = null;
option.GetDialog = () => resultDialog;
option.BodyTemplate = builder =>
{
builder.AddMultipleAttributes(1, option.ComponentParameters);
}
builder.AddComponentReferenceCapture(index++, com => resultDialog = (IResultDialog)com);
builder.CloseComponent();
};
builder.OpenComponent(0, typeof(TDialog));
if (option.ComponentParameters != null)
{
builder.AddMultipleAttributes(10, option.ComponentParameters);
}
builder.AddComponentReferenceCapture(30, com => resultDialog = (IResultDialog)com);
builder.CloseComponent();
};
}
option.FooterTemplate = BootstrapDynamicComponent.CreateComponent<ResultDialogFooter>(new Dictionary<string, object?>
option.FooterTemplate ??= BootstrapDynamicComponent.CreateComponent<ResultDialogFooter>(new Dictionary<string, object?>
{
[nameof(ResultDialogFooter.ButtonNoText)] = option.ButtonNoText,
[nameof(ResultDialogFooter.ButtonYesText)] = option.ButtonYesText,
@ -105,6 +109,52 @@ public static class DialogServiceExtensions
return await option.ResultTask.Task;
}
/// <summary>
/// 弹出带结果的对话框
/// </summary>
/// <param name="service">DialogService 服务实例</param>
/// <param name="title">对话框标题,优先级高于 <see cref="DialogOption.Title"/></param>
/// <param name="content">对话框 <see cref="MarkupString"/> 文本参数</param>
/// <param name="option"><see cref="ResultDialogOption"/> 对话框参数实例</param>
/// <param name="dialog">指定弹窗组件 默认为 null 使用 <see cref="BootstrapBlazorRoot"/> 组件内置弹窗组件</param>
public static Task<DialogResult> ShowModal(this DialogService service, string title, string content, ResultDialogOption? option = null, Dialog? dialog = null)
{
option ??= new();
if (!string.IsNullOrEmpty(title))
{
option.Title = title;
}
if (!string.IsNullOrEmpty(content))
{
IResultDialog? resultDialog = null;
option.GetDialog = () => resultDialog;
option.BodyTemplate = builder =>
{
builder.OpenComponent(0, typeof(ResultDialog));
builder.AddAttribute(20, nameof(ResultDialog.Content), content);
builder.AddComponentReferenceCapture(30, com => resultDialog = (IResultDialog)com);
builder.CloseComponent();
};
}
return ShowModal<ResultDialog>(service, option, dialog);
}
private class ResultDialog : ComponentBase, IResultDialog
{
[Parameter]
public string? Content { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddMarkupContent(0, Content);
}
public Task OnClose(DialogResult result)
{
return Task.CompletedTask;
}
}
/// <summary>
/// 弹出带保存按钮对话窗方法
/// </summary>

View File

@ -7,6 +7,24 @@ namespace UnitTest.Components;
public class DialogTest : BootstrapBlazorTestBase
{
[Fact]
public async Task ShowModal_Ok()
{
var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb =>
{
pb.Add(a => a.EnableErrorLogger, false);
pb.AddChildContent<MockDialogTest>();
});
var modal = cut.FindComponent<Modal>();
var dialog = cut.FindComponent<MockDialogTest>().Instance.DialogService;
_ = cut.InvokeAsync(() => dialog.ShowModal("title", "<span class=\"text-danger\">test-content</span>"));
cut.WaitForState(() => cut.Markup.Contains("btn-primary"));
var closeButton = cut.Find(".btn-primary");
await cut.InvokeAsync(() => closeButton.Click());
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
}
[Fact]
public async Task Show_Ok()
{
@ -304,6 +322,7 @@ public class DialogTest : BootstrapBlazorTestBase
// 点击的是 No 按钮
result = true;
resultOption.BodyTemplate = null;
_ = cut.InvokeAsync(() => dialog.ShowModal<MockModalDialog>(resultOption));
cut.WaitForState(() => cut.Markup.Contains("btn-danger"));
@ -314,6 +333,7 @@ public class DialogTest : BootstrapBlazorTestBase
// 点击的是 Close 按钮
result = true;
resultOption.BodyTemplate = null;
_ = cut.InvokeAsync(() => dialog.ShowModal<MockModalDialog>(resultOption));
cut.WaitForState(() => cut.Markup.Contains("btn-secondary"));
@ -341,6 +361,7 @@ public class DialogTest : BootstrapBlazorTestBase
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
// 点击右上角关闭按钮
resultOption.BodyTemplate = null;
_ = cut.InvokeAsync(() => dialog.ShowModal<MockModalDialog>(resultOption));
cut.WaitForState(() => cut.Markup.Contains("btn-close"));
@ -349,12 +370,21 @@ public class DialogTest : BootstrapBlazorTestBase
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
// 点击 FooterTemplate 中的 关闭 按钮
resultOption.BodyTemplate = null;
_ = cut.InvokeAsync(() => dialog.ShowModal<MockModalDialogClosingFalse>(resultOption));
cut.WaitForState(() => cut.Markup.Contains("btn-secondary"));
closeButton = cut.Find(".btn-secondary");
await cut.InvokeAsync(() => closeButton.Click());
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
// 测试 Markup 扩展模式弹窗
_ = cut.InvokeAsync(() => dialog.ShowModal("title", "<span class=\"text-danger\">test-content</span>"));
cut.WaitForState(() => cut.Markup.Contains("btn-primary"));
closeButton = cut.Find(".btn-primary");
await cut.InvokeAsync(() => closeButton.Click());
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
#endregion
#region

View File

@ -48,5 +48,9 @@ public class ClipboardServiceTest : BootstrapBlazorTestBase
items = await service.Get();
item = items[0];
Assert.Empty(item.Text);
Context.JSInterop.Setup<List<ClipboardItem>?>("getAllClipboardContents").SetResult(null);
items = await service.Get();
Assert.Empty(items);
}
}