Skip to content

Commit 74a74d3

Browse files
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 <[email protected]>
1 parent 3a939f2 commit 74a74d3

File tree

7 files changed

+113
-15
lines changed

7 files changed

+113
-15
lines changed

src/BootstrapBlazor.Server/Components/Samples/Dialogs.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ private async Task OnClick()
124124
<li>@Localizer["ModalDialogTip5"]</li>
125125
</ul>
126126
</section>
127-
<Button OnClick="@OnResultDialogClick">@Localizer["ModalDialogButton"]</Button>
127+
<Button Text="@Localizer["ModalDialogButton"]" OnClick="@OnResultDialogClick"></Button>
128+
<Button Text="@Localizer["ConfirmDialogButton"]" OnClick="@OnConfirmModalClick"></Button>
128129
<ConsoleLogger @ref="ModalDialogLogger" />
129130
</DemoBlock>
130131

src/BootstrapBlazor.Server/Components/Samples/Dialogs.razor.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ private async Task OnResultDialogClick()
163163
ModalDialogLogger.Log($"The return value of the popup window is: {result} The return value of the component is: {DemoValue1}");
164164
}
165165

166+
private async Task OnConfirmModalClick()
167+
{
168+
var result = await DialogService.ShowModal(Localizer["ConfirmDialogModalContent"], Localizer["ConfirmDialogModalTitle"]);
169+
170+
ModalDialogLogger.Log($"The return value of the popup window is: {result} no component provider");
171+
}
172+
166173
private async Task OnEditDialogClick()
167174
{
168175
var option = new EditDialogOption<Foo>()

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,10 @@
902902
"ExportPdfDialogTitle": "Pop up window with export Pdf function",
903903
"ExportPdfDialogIntro": "Set <code>ShowExportPdfButtonInHeader</code> to display an export PDF button on the <code>Header</code>",
904904
"ExportPdfDialogTip": "More parameters can be set by setting <code>ExportPdfButtonOptions</code>",
905-
"ExportPdfButton": "Export Pdf"
905+
"ExportPdfButton": "Export Pdf",
906+
"ConfirmDialogButton": "Popup Modal",
907+
"ConfirmDialogModalTitle": "Literal Confirmation Modal",
908+
"ConfirmDialogModalContent": "<p>this is the prompt message. </p><div class=\"text-danger\">this is a danger info</div>"
906909
},
907910
"BootstrapBlazor.Server.Components.Samples.Dispatches": {
908911
"Title": "Dispatch message distribution",

src/BootstrapBlazor.Server/Locales/zh-CN.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,10 @@
902902
"ExportPdfDialogTitle": "带导出 Pdf 功能的弹窗",
903903
"ExportPdfDialogIntro": "通过设置 <code>ShowExportPdfButtonInHeader</code> 使 <code>Header</code> 上显示一个导出 pdf 按钮",
904904
"ExportPdfDialogTip": "可通过设置 <code>ExportPdfButtonOptions</code> 对更多参数进行设置",
905-
"ExportPdfButton": "导出 Pdf 弹窗"
905+
"ExportPdfButton": "导出 Pdf 弹窗",
906+
"ConfirmDialogButton": "弹出模态框",
907+
"ConfirmDialogModalTitle": "文字确认模态框",
908+
"ConfirmDialogModalContent": "<p>这是一个文字确认模态框</p><div class=\"text-danger\">这是警告信息</div>"
906909
},
907910
"BootstrapBlazor.Server.Components.Samples.Dispatches": {
908911
"Title": "Dispatch 消息分发",

src/BootstrapBlazor/Extensions/DialogServiceExtensions.cs

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// See the LICENSE file in the project root for more information.
44
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
55

6+
using Microsoft.AspNetCore.Components.Rendering;
7+
68
namespace BootstrapBlazor.Components;
79

810
/// <summary>
@@ -71,21 +73,23 @@ public static async Task ShowEditDialog<TModel>(this DialogService service, Edit
7173
public static async Task<DialogResult> ShowModal<TDialog>(this DialogService service, ResultDialogOption option, Dialog? dialog = null)
7274
where TDialog : IComponent, IResultDialog
7375
{
74-
IResultDialog? resultDialog = null;
75-
option.GetDialog = () => resultDialog;
76-
option.BodyTemplate = builder =>
76+
if (option.BodyTemplate == null)
7777
{
78-
var index = 0;
79-
builder.OpenComponent(index++, typeof(TDialog));
80-
if (option.ComponentParameters != null)
78+
IResultDialog? resultDialog = null;
79+
option.GetDialog = () => resultDialog;
80+
option.BodyTemplate = builder =>
8181
{
82-
builder.AddMultipleAttributes(1, option.ComponentParameters);
83-
}
84-
builder.AddComponentReferenceCapture(index++, com => resultDialog = (IResultDialog)com);
85-
builder.CloseComponent();
86-
};
82+
builder.OpenComponent(0, typeof(TDialog));
83+
if (option.ComponentParameters != null)
84+
{
85+
builder.AddMultipleAttributes(10, option.ComponentParameters);
86+
}
87+
builder.AddComponentReferenceCapture(30, com => resultDialog = (IResultDialog)com);
88+
builder.CloseComponent();
89+
};
90+
}
8791

88-
option.FooterTemplate = BootstrapDynamicComponent.CreateComponent<ResultDialogFooter>(new Dictionary<string, object?>
92+
option.FooterTemplate ??= BootstrapDynamicComponent.CreateComponent<ResultDialogFooter>(new Dictionary<string, object?>
8993
{
9094
[nameof(ResultDialogFooter.ButtonNoText)] = option.ButtonNoText,
9195
[nameof(ResultDialogFooter.ButtonYesText)] = option.ButtonYesText,
@@ -105,6 +109,52 @@ public static async Task<DialogResult> ShowModal<TDialog>(this DialogService ser
105109
return await option.ResultTask.Task;
106110
}
107111

112+
/// <summary>
113+
/// 弹出带结果的对话框
114+
/// </summary>
115+
/// <param name="service">DialogService 服务实例</param>
116+
/// <param name="title">对话框标题,优先级高于 <see cref="DialogOption.Title"/></param>
117+
/// <param name="content">对话框 <see cref="MarkupString"/> 文本参数</param>
118+
/// <param name="option"><see cref="ResultDialogOption"/> 对话框参数实例</param>
119+
/// <param name="dialog">指定弹窗组件 默认为 null 使用 <see cref="BootstrapBlazorRoot"/> 组件内置弹窗组件</param>
120+
public static Task<DialogResult> ShowModal(this DialogService service, string title, string content, ResultDialogOption? option = null, Dialog? dialog = null)
121+
{
122+
option ??= new();
123+
if (!string.IsNullOrEmpty(title))
124+
{
125+
option.Title = title;
126+
}
127+
if (!string.IsNullOrEmpty(content))
128+
{
129+
IResultDialog? resultDialog = null;
130+
option.GetDialog = () => resultDialog;
131+
option.BodyTemplate = builder =>
132+
{
133+
builder.OpenComponent(0, typeof(ResultDialog));
134+
builder.AddAttribute(20, nameof(ResultDialog.Content), content);
135+
builder.AddComponentReferenceCapture(30, com => resultDialog = (IResultDialog)com);
136+
builder.CloseComponent();
137+
};
138+
}
139+
return ShowModal<ResultDialog>(service, option, dialog);
140+
}
141+
142+
private class ResultDialog : ComponentBase, IResultDialog
143+
{
144+
[Parameter]
145+
public string? Content { get; set; }
146+
147+
protected override void BuildRenderTree(RenderTreeBuilder builder)
148+
{
149+
builder.AddMarkupContent(0, Content);
150+
}
151+
152+
public Task OnClose(DialogResult result)
153+
{
154+
return Task.CompletedTask;
155+
}
156+
}
157+
108158
/// <summary>
109159
/// 弹出带保存按钮对话窗方法
110160
/// </summary>

test/UnitTest/Components/DialogTest.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ namespace UnitTest.Components;
77

88
public class DialogTest : BootstrapBlazorTestBase
99
{
10+
[Fact]
11+
public async Task ShowModal_Ok()
12+
{
13+
var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb =>
14+
{
15+
pb.Add(a => a.EnableErrorLogger, false);
16+
pb.AddChildContent<MockDialogTest>();
17+
});
18+
var modal = cut.FindComponent<Modal>();
19+
var dialog = cut.FindComponent<MockDialogTest>().Instance.DialogService;
20+
_ = cut.InvokeAsync(() => dialog.ShowModal("title", "<span class=\"text-danger\">test-content</span>"));
21+
cut.WaitForState(() => cut.Markup.Contains("btn-primary"));
22+
23+
var closeButton = cut.Find(".btn-primary");
24+
await cut.InvokeAsync(() => closeButton.Click());
25+
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
26+
}
27+
1028
[Fact]
1129
public async Task Show_Ok()
1230
{
@@ -304,6 +322,7 @@ await cut.InvokeAsync(() => dialog.Show(new DialogOption()
304322

305323
// 点击的是 No 按钮
306324
result = true;
325+
resultOption.BodyTemplate = null;
307326
_ = cut.InvokeAsync(() => dialog.ShowModal<MockModalDialog>(resultOption));
308327
cut.WaitForState(() => cut.Markup.Contains("btn-danger"));
309328

@@ -314,6 +333,7 @@ await cut.InvokeAsync(() => dialog.Show(new DialogOption()
314333

315334
// 点击的是 Close 按钮
316335
result = true;
336+
resultOption.BodyTemplate = null;
317337
_ = cut.InvokeAsync(() => dialog.ShowModal<MockModalDialog>(resultOption));
318338
cut.WaitForState(() => cut.Markup.Contains("btn-secondary"));
319339

@@ -341,6 +361,7 @@ await cut.InvokeAsync(() => dialog.Show(new DialogOption()
341361
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
342362

343363
// 点击右上角关闭按钮
364+
resultOption.BodyTemplate = null;
344365
_ = cut.InvokeAsync(() => dialog.ShowModal<MockModalDialog>(resultOption));
345366
cut.WaitForState(() => cut.Markup.Contains("btn-close"));
346367

@@ -349,12 +370,21 @@ await cut.InvokeAsync(() => dialog.Show(new DialogOption()
349370
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
350371

351372
// 点击 FooterTemplate 中的 关闭 按钮
373+
resultOption.BodyTemplate = null;
352374
_ = cut.InvokeAsync(() => dialog.ShowModal<MockModalDialogClosingFalse>(resultOption));
353375
cut.WaitForState(() => cut.Markup.Contains("btn-secondary"));
354376

355377
closeButton = cut.Find(".btn-secondary");
356378
await cut.InvokeAsync(() => closeButton.Click());
357379
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
380+
381+
// 测试 Markup 扩展模式弹窗
382+
_ = cut.InvokeAsync(() => dialog.ShowModal("title", "<span class=\"text-danger\">test-content</span>"));
383+
cut.WaitForState(() => cut.Markup.Contains("btn-primary"));
384+
385+
closeButton = cut.Find(".btn-primary");
386+
await cut.InvokeAsync(() => closeButton.Click());
387+
await cut.InvokeAsync(() => modal.Instance.CloseCallback());
358388
#endregion
359389

360390
#region 弹窗中的弹窗测试

test/UnitTest/Services/ClipboardServiceTest.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,9 @@ public async Task Get()
4848
items = await service.Get();
4949
item = items[0];
5050
Assert.Empty(item.Text);
51+
52+
Context.JSInterop.Setup<List<ClipboardItem>?>("getAllClipboardContents").SetResult(null);
53+
items = await service.Get();
54+
Assert.Empty(items);
5155
}
5256
}

0 commit comments

Comments
 (0)