Skip to content

Commit 1905435

Browse files
authored
feat(Dropdown): add ItemsTemplate parameter (#7046)
* feat: 增加 DropdownItem 组件 * feat: 增加 IDropdownItem 接口 * feat: 增加 DropdownItem 组件 * feat: 增加 DropdownDivider 组件 * feat: 实现 ItemsTemplate 模板功能 * refactor: 代码格式化 * feat: 移除 IDropdownItem 接口 * doc: 更新示例 * doc: 增加 ItemsTemplate 示例 * doc: 增加多语言 * refactor: 移除 DropdownDivider 组件 * refactor: 精简代码 * refactor: 更新示例 * refactor: 更改属性名 * test: 增加单元测试 * refactor: 代码格式化 * doc: 代码格式化 * refactor: 移除变量 * refactor: 增加条件渲染 * refactor: 增加 ButtonText 判断
1 parent 2f53676 commit 1905435

File tree

9 files changed

+187
-15
lines changed

9 files changed

+187
-15
lines changed

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
</div>
3232
</DemoBlock>
3333

34-
<DemoBlock Title="@Localizer["DropdownItemTemplateTitle"]" Introduction="@Localizer["DropdownItemTemplateIntro"]" Name="ItemTemplate">
34+
<DemoBlock Title="@Localizer["DropdownItemTemplateTitle"]" Introduction="@Localizer["DropdownItemTemplateIntro"]"
35+
Name="ItemTemplate">
3536
<Dropdown TValue="string" Items="ItemTemplateList">
3637
<ItemTemplate>
3738
<Tooltip Title="just a tooltip text demo" Trigger="hover">
@@ -42,6 +43,24 @@
4243
</Dropdown>
4344
</DemoBlock>
4445

46+
<DemoBlock Title="@Localizer["DropdownItemsTemplateTitle"]" Introduction="@Localizer["DropdownItemsTemplateIntro"]"
47+
Name="ItemsTemplate">
48+
<Dropdown TValue="string" IsFixedButtonText="true" FixedButtonText="More" Icon="fa solid fa-ellipsis">
49+
<ItemsTemplate>
50+
<DropdownItem Text="Copy" Icon="fa-solid fa-copy" OnClick="@(() => OnClickAction("Copy"))"></DropdownItem>
51+
<DropdownItem Text="Paste" Icon="fa-solid fa-paste" OnClick="@(() => OnClickAction("Paste"))"></DropdownItem>
52+
<Divider></Divider>
53+
<DropdownItem Text="Action1" Icon="fa-solid fa-flag" OnClick="@(() => OnClickAction("Action1"))"></DropdownItem>
54+
<DropdownItem>
55+
<Tooltip Title="just a tooltip text demo" Trigger="hover">
56+
<span class="fa-solid fa-flag"></span>
57+
<div class="ms-2 flex-fill" @onclick="@(() => OnClickAction("Action2"))">Action2</div>
58+
</Tooltip>
59+
</DropdownItem>
60+
</ItemsTemplate>
61+
</Dropdown>
62+
</DemoBlock>
63+
4564
<DemoBlock Title="@Localizer["SplitTitle"]" Introduction='@Localizer["SplitIntro"]' Name="Split">
4665
<div class="row g-3">
4766
<div class="col-6 col-sm-4 col-md-3">

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ private async Task OnIsAsyncClick()
163163
await ToastService.Success("Dropdown IsAsync", "Job done!");
164164
}
165165

166+
private Task OnClickAction(string actionName) => ToastService.Information("Custom Action", $"Trigger {actionName}");
167+
166168
/// <summary>
167169
/// GetAttributes
168170
/// </summary>

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1626,7 +1626,9 @@
16261626
"DropdownCascadeItem7": "Jingan",
16271627
"DropdownCascadeItem8": "Huangpu",
16281628
"DropdownItemTemplateTitle": "Item Template",
1629-
"DropdownItemTemplateIntro": "By setting <code>ItemTemplate</code>, you can customize the content displayed in the drop-down item. In this example, the <code>Tooltip</code> component is used to add a tooltip function when the mouse is hovered."
1629+
"DropdownItemTemplateIntro": "By setting <code>ItemTemplate</code>, you can customize the content displayed in the drop-down item. In this example, the <code>Tooltip</code> component is used to add a tooltip function when the mouse is hovered.",
1630+
"DropdownItemsTemplateTitle": "Items Template",
1631+
"DropdownItemsTemplateIntro": "You can customize all the content of the dropdown list by setting `<itemsTemplate>`. In this example, we use the `<DropdownItem>` component and `<DropdownDivider>` component to customize the dropdown list component."
16301632
},
16311633
"BootstrapBlazor.Server.Components.Samples.GoTops": {
16321634
"Title": "GoTop",

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1626,7 +1626,9 @@
16261626
"DropdownCascadeItem7": "静安区",
16271627
"DropdownCascadeItem8": "黄浦区",
16281628
"DropdownItemTemplateTitle": "下拉项模板",
1629-
"DropdownItemTemplateIntro": "通过设置 <code>ItemTemplate</code> 可以自定义下拉项显示内容,本例中通过自定义模板使用 <code>Tooltip</code> 组件增加鼠标悬浮是显示提示功能"
1629+
"DropdownItemTemplateIntro": "通过设置 <code>ItemTemplate</code> 可以自定义下拉项显示内容,本例中通过自定义模板使用 <code>Tooltip</code> 组件增加鼠标悬浮是显示提示功能",
1630+
"DropdownItemsTemplateTitle": "下拉框模板",
1631+
"DropdownItemsTemplateIntro": "通过设置 <code>ItemsTemplate</code> 可以自定义下拉框所有内容,本例中通过使用 <code>DropdownItem</code> 组件与 <code>DropdownDivider</code> 对下拉框组件进行自定义"
16301632
},
16311633
"BootstrapBlazor.Server.Components.Samples.GoTops": {
16321634
"Title": "GoTop 返回顶端组件",

src/BootstrapBlazor/Components/Dropdown/Dropdown.razor

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
<BootstrapLabel for="@Id" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText"></BootstrapLabel>
99
}
1010
<div @attributes="@AdditionalAttributes" id="@Id" class="@DirectionClassName">
11-
<DynamicElement TagName="button" type="button" class="@ButtonClassName" data-bs-toggle="@DropdownToggle" disabled="@Disabled"
11+
<DynamicElement TagName="button" type="button" class="@ButtonClassName" data-bs-toggle="@DropdownToggle"
12+
disabled="@Disabled"
1213
TriggerClick="ShowSplit" OnClick="OnClickButton" PreventDefault="false" StopPropagation="false">
1314
@if (ButtonTemplate == null)
1415
{
@@ -20,7 +21,10 @@
2021
{
2122
<i class="@Icon"></i>
2223
}
23-
<span>@ButtonText</span>
24+
@if (!string.IsNullOrEmpty(ButtonText))
25+
{
26+
<span>@ButtonText</span>
27+
}
2428
}
2529
else
2630
{
@@ -29,7 +33,9 @@
2933
</DynamicElement>
3034
@if (ShowSplit)
3135
{
32-
<button type="button" class="@ClassName" data-bs-toggle="@ToggleString" disabled="@Disabled" aria-haspopup="true" aria-expanded="false"></button>
36+
<button type="button" class="@ClassName" data-bs-toggle="@ToggleString" disabled="@Disabled"
37+
aria-haspopup="true" aria-expanded="false">
38+
</button>
3339
}
3440
<div class="@MenuAlignmentClass">
3541
@if (ItemsTemplate == null)

src/BootstrapBlazor/Components/Dropdown/Dropdown.razor.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@
99
margin-inline-start: 0.25rem;
1010
}
1111
}
12+
13+
.dropdown-menu {
14+
.divider {
15+
margin: 0.25rem 0;
16+
}
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@namespace BootstrapBlazor.Components
2+
3+
@if (ChildContent != null)
4+
{
5+
<div class="@ItemClassString">
6+
@ChildContent
7+
</div>
8+
}
9+
else
10+
{
11+
<DynamicElement class="@ItemClassString"
12+
TriggerClick="!IsDisabled" OnClick="OnClickItem">
13+
@if (!string.IsNullOrEmpty(ItemIconString))
14+
{
15+
<i class="@ItemIconString"></i>
16+
}
17+
@if (!string.IsNullOrEmpty(Text))
18+
{
19+
<span>@Text</span>
20+
}
21+
</DynamicElement>
22+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Components;
7+
8+
/// <summary>
9+
/// DropdownItem 组件
10+
/// </summary>
11+
public partial class DropdownItem
12+
{
13+
/// <summary>
14+
/// 获得/设置 显示文本
15+
/// </summary>
16+
[Parameter]
17+
public string? Text { get; set; }
18+
19+
/// <summary>
20+
/// 获得/设置 图标
21+
/// </summary>
22+
[Parameter]
23+
public string? Icon { get; set; }
24+
25+
/// <summary>
26+
/// 获得/设置 是否被禁用 默认 false 优先级低于 <see cref="OnDisabledCallback"/>
27+
/// </summary>
28+
[Parameter]
29+
public bool Disabled { get; set; }
30+
31+
/// <summary>
32+
/// 获得/设置 是否被禁用回调方法 默认 null 优先级高于 <see cref="Disabled"/>
33+
/// </summary>
34+
[Parameter]
35+
public Func<bool>? OnDisabledCallback { get; set; }
36+
37+
/// <summary>
38+
/// 获得/设置 点击回调方法 默认 null
39+
/// </summary>
40+
[Parameter]
41+
public Func<Task>? OnClick { get; set; }
42+
43+
/// <summary>
44+
/// 获得/设置 组件内容
45+
/// </summary>
46+
[Parameter]
47+
public RenderFragment? ChildContent { get; set; }
48+
49+
private string? ItemIconString => CssBuilder.Default("dropdown-item-icon")
50+
.AddClass(Icon, !string.IsNullOrEmpty(Icon))
51+
.Build();
52+
53+
private string? ItemClassString => CssBuilder.Default("dropdown-item")
54+
.AddClass("disabled", IsDisabled)
55+
.Build();
56+
57+
private bool IsDisabled => OnDisabledCallback?.Invoke() ?? Disabled;
58+
59+
private async Task OnClickItem()
60+
{
61+
if (OnClick != null)
62+
{
63+
await OnClick();
64+
}
65+
}
66+
}

test/UnitTest/Components/DropdownTest.cs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,7 @@ public async Task IsAsync_Ok()
4242
pb.Add(a => a.IsAsync, true);
4343
pb.Add(a => a.IsKeepDisabled, false);
4444
pb.Add(a => a.Icon, "fa-solid fa-test-icon");
45-
pb.Add(a => a.OnClickWithoutRender, () =>
46-
{
47-
return Task.CompletedTask;
48-
});
45+
pb.Add(a => a.OnClickWithoutRender, () => Task.CompletedTask);
4946
});
5047
cut.Contains("<i class=\"fa-solid fa-test-icon\"></i>");
5148
var button = cut.Find("button");
@@ -272,16 +269,66 @@ public void Disabled_Ok()
272269
}
273270

274271
[Fact]
275-
public void ItemsTemplate_Ok()
272+
public async Task ItemsTemplate_Ok()
276273
{
274+
var clicked = false;
277275
var cut = Context.RenderComponent<Dropdown<string>>(pb =>
278276
{
279-
pb.Add(a => a.ItemsTemplate, new RenderFragment(builder =>
277+
pb.Add(a => a.IsFixedButtonText, true);
278+
pb.Add(a => a.FixedButtonText, "fixed text");
279+
pb.Add(a => a.ItemsTemplate, builder =>
280280
{
281-
builder.AddContent(0, new MarkupString("<div>test-items-template</div>"));
282-
}));
281+
builder.OpenComponent<DropdownItem>(0);
282+
builder.AddAttribute(1, "Icon", "fa-solid fa-icon1");
283+
builder.AddAttribute(2, "Text", "dropdown-item-test1");
284+
builder.AddAttribute(3, "OnClick", () =>
285+
{
286+
clicked = true;
287+
return Task.CompletedTask;
288+
});
289+
builder.CloseComponent();
290+
291+
builder.OpenComponent<DropdownItem>(0);
292+
builder.AddAttribute(1, "Icon", "fa-solid fa-icon2");
293+
builder.AddAttribute(2, "Text", "dropdown-item-test2");
294+
builder.AddAttribute(3, "Disabled", true);
295+
builder.CloseComponent();
296+
297+
builder.OpenComponent<DropdownItem>(0);
298+
builder.AddAttribute(1, "Icon", "fa-solid fa-icon3");
299+
builder.AddAttribute(2, "Text", "dropdown-item-test3");
300+
builder.AddAttribute(3, "Disabled", false);
301+
builder.AddAttribute(4, "OnDisabledCallback", () => true);
302+
builder.CloseComponent();
303+
304+
builder.OpenComponent<DropdownItem>(0);
305+
builder.AddAttribute(1, "Icon", "fa-solid fa-icon4");
306+
builder.AddAttribute(2, "Text", "dropdown-item-test4");
307+
builder.AddAttribute(3, "ChildContent",
308+
new RenderFragment(pb1 => pb1.AddMarkupContent(0, "<div>dropdown-item-childcontent</div>")));
309+
builder.CloseComponent();
310+
});
283311
});
284-
cut.Contains("<div>test-items-template</div>");
312+
cut.Contains("fa-solid fa-icon1");
313+
cut.Contains("dropdown-item-test1");
314+
315+
cut.Contains("fa-solid fa-icon2");
316+
cut.Contains("dropdown-item-test2");
317+
318+
cut.Contains("fa-solid fa-icon3");
319+
cut.Contains("dropdown-item-test3");
320+
321+
cut.DoesNotContain("fa-solid fa-icon4");
322+
cut.DoesNotContain("dropdown-item-test4");
323+
cut.Contains("dropdown-item-childcontent");
324+
325+
cut.Contains("dropdown-item disabled");
326+
327+
Assert.False(clicked);
328+
329+
var item = cut.Find(".dropdown-item");
330+
await cut.InvokeAsync((() => item.Click()));
331+
Assert.True(clicked);
285332
}
286333

287334
[Fact]

0 commit comments

Comments
 (0)