From 56caf255346cd6ecd893ee95ec96a09d0af3453b Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 22 Nov 2025 02:20:05 +0330 Subject: [PATCH 01/39] feat(blazorui): add missing tests for BitPullToRefresh component #11710 (#11711) --- .../PullToRefresh/BitPullToRefreshTests.cs | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/PullToRefresh/BitPullToRefreshTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/PullToRefresh/BitPullToRefreshTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/PullToRefresh/BitPullToRefreshTests.cs new file mode 100644 index 0000000000..ac287b8d26 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/PullToRefresh/BitPullToRefreshTests.cs @@ -0,0 +1,142 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Utilities.PullToRefresh; + +[TestClass] +public class BitPullToRefreshTests : BunitTestContext +{ + [TestMethod] + public void BitPullToRefreshShouldRenderStructure() + { + Context.JSInterop.SetupVoid("BitBlazorUI.PullToRefresh.setup"); + + var component = RenderComponent(); + + var root = component.Find(".bit-ptr"); + Assert.IsNotNull(root); + + var loading = component.Find(".bit-ptr-lod"); + var spinnerWrapper = component.Find(".bit-ptr-spw"); + var spinner = component.Find(".bit-ptr-spn"); + + Assert.IsNotNull(loading); + Assert.IsNotNull(spinnerWrapper); + Assert.IsNotNull(spinner); + } + + [TestMethod] + public void BitPullToRefreshShouldInvokeOnRefresh() + { + Context.JSInterop.SetupVoid("BitBlazorUI.PullToRefresh.setup"); + + var refreshed = false; + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnRefresh, EventCallback.Factory.Create(this, () => refreshed = true)); + }); + + component.Instance._Refresh().GetAwaiter().GetResult(); + + Assert.IsTrue(refreshed); + } + + [TestMethod] + public void BitPullToRefreshShouldInvokePullCallbacks() + { + Context.JSInterop.SetupVoid("BitBlazorUI.PullToRefresh.setup"); + + BitPullToRefreshPullStartArgs? startArgs = null; + decimal moveDiff = 0; + decimal endDiff = 0; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnPullStart, EventCallback.Factory.Create(this, args => startArgs = args)); + parameters.Add(p => p.OnPullMove, EventCallback.Factory.Create(this, diff => moveDiff = diff)); + parameters.Add(p => p.OnPullEnd, EventCallback.Factory.Create(this, diff => endDiff = diff)); + }); + + component.Instance._OnStart(10m, 20m, 100m).GetAwaiter().GetResult(); + component.Instance._OnMove(80m).GetAwaiter().GetResult(); + component.Instance._OnEnd(60m).GetAwaiter().GetResult(); + + Assert.IsNotNull(startArgs); + Assert.AreEqual(10m, startArgs!.Top); + Assert.AreEqual(20m, startArgs.Left); + Assert.AreEqual(100m, startArgs.Width); + + Assert.AreEqual(80m, moveDiff); + Assert.AreEqual(60m, endDiff); + } + + [TestMethod] + public void BitPullToRefreshShouldRespectClassesAndStyles() + { + Context.JSInterop.SetupVoid("BitBlazorUI.PullToRefresh.setup"); + + var classes = new BitPullToRefreshClassStyles + { + Root = "custom-root", + Loading = "custom-loading", + SpinnerWrapper = "custom-spw", + Spinner = "custom-spn" + }; + + var styles = new BitPullToRefreshClassStyles + { + Loading = "background:red;", + SpinnerWrapper = "background:cyan;", + Spinner = "color:green;" + }; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Classes, classes); + parameters.Add(p => p.Styles, styles); + }); + + component.Instance._OnMove(80m).GetAwaiter().GetResult(); + + var root = component.Find(".bit-ptr"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + + var loading = component.Find(".bit-ptr-lod"); + Assert.IsTrue(loading.ClassList.Contains("custom-loading")); + Assert.AreEqual("background:red;", loading.GetAttribute("style")); + + var spinnerWrapper = component.Find(".bit-ptr-spw"); + Assert.IsTrue(spinnerWrapper.ClassList.Contains("custom-spw")); + StringAssert.Contains(spinnerWrapper.GetAttribute("style"), "background:cyan"); + + var spinner = component.Find(".bit-ptr-spn"); + Assert.IsTrue(spinner.ClassList.Contains("custom-spn")); + StringAssert.Contains(spinner.GetAttribute("style"), "color:green"); + } + + [TestMethod] + public void BitPullToRefreshShouldRenderChildContent() + { + Context.JSInterop.SetupVoid("BitBlazorUI.PullToRefresh.setup"); + + var component = RenderComponent(parameters => + { + parameters.AddChildContent("
content
"); + }); + + var content = component.Find(".anchor"); + Assert.IsNotNull(content); + Assert.AreEqual("content", content.TextContent); + } + + [TestMethod] + public void BitPullToRefreshShouldCallJsSetupOnFirstRender() + { + Context.JSInterop.SetupVoid("BitBlazorUI.PullToRefresh.setup"); + + RenderComponent(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.PullToRefresh.setup"); + } +} From b8f2a91b0817127e0e74783ebf064757810d4879 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 22 Nov 2025 11:26:49 +0330 Subject: [PATCH 02/39] feat(blazorui): add missing tests for BitSwipeTrap #11712 (#11713) --- .../Utilities/SwipeTrap/BitSwipeTrapTests.cs | 194 ++++++++++++++++++ .../Components/BitComponentBase.cs | 3 +- 2 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/SwipeTrap/BitSwipeTrapTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/SwipeTrap/BitSwipeTrapTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/SwipeTrap/BitSwipeTrapTests.cs new file mode 100644 index 0000000000..1139daebff --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Utilities/SwipeTrap/BitSwipeTrapTests.cs @@ -0,0 +1,194 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Bunit; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Utilities.SwipeTrap; + +[TestClass] +public class BitSwipeTrapTests : BunitTestContext +{ + [TestMethod] + public void BitSwipeTrapShouldRenderChildContent() + { + var component = RenderComponent(parameters => + { + parameters.AddChildContent("

Swipe me

"); + }); + + component.MarkupMatches(@"

Swipe me

"); + } + + [TestMethod, + DataRow(true), + DataRow(false)] + public void BitSwipeTrapShouldRespectIsEnabled(bool isEnabled) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + }); + + var root = component.Find(".bit-stp"); + + if (isEnabled) + { + Assert.IsFalse(root.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + } + + [TestMethod, + DataRow(BitDir.Rtl), + DataRow(BitDir.Ltr), + DataRow(BitDir.Auto), + DataRow(null)] + public void BitSwipeTrapShouldRespectDir(BitDir? dir) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Dir, dir); + }); + + if (dir.HasValue) + { + var rtlClass = dir is BitDir.Rtl ? " bit-rtl" : null; + component.MarkupMatches(@$"
"); + } + else + { + component.MarkupMatches(@"
"); + } + } + + [TestMethod, + DataRow("custom-id"), + DataRow(null)] + public void BitSwipeTrapShouldRespectId(string id) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Id, id); + }); + + var expectedId = id.HasValue() ? id : component.Instance.UniqueId.ToString(); + + component.MarkupMatches(@$"
"); + } + + [TestMethod] + public void BitSwipeTrapShouldRespectAriaLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.AriaLabel, "swipe area"); + }); + + component.MarkupMatches(@"
"); + } + + [TestMethod] + public void BitSwipeTrapShouldCallJsSetupOnFirstRender() + { + Context.JSInterop.SetupVoid("BitBlazorUI.SwipeTrap.setup"); + + RenderComponent(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.SwipeTrap.setup"); + } + + [TestMethod] + public async Task BitSwipeTrapShouldDisposeJsInteropOnDispose() + { + Context.JSInterop.SetupVoid("BitBlazorUI.SwipeTrap.setup"); + Context.JSInterop.SetupVoid("BitBlazorUI.SwipeTrap.dispose"); + + var component = RenderComponent(); + + await component.Instance.DisposeAsync(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.SwipeTrap.dispose"); + } + + [TestMethod] + public async Task BitSwipeTrapShouldInvokeOnStart() + { + BitSwipeTrapEventArgs? eventArgs = null; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnStart, (BitSwipeTrapEventArgs args) => eventArgs = args); + }); + + await component.Instance._OnStart(10, 20); + + Assert.IsNotNull(eventArgs); + Assert.AreEqual(10, eventArgs!.StartX); + Assert.AreEqual(20, eventArgs.StartY); + Assert.AreEqual(0, eventArgs.DiffX); + Assert.AreEqual(0, eventArgs.DiffY); + } + + [TestMethod] + public async Task BitSwipeTrapShouldInvokeOnMove() + { + BitSwipeTrapEventArgs? eventArgs = null; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnMove, (BitSwipeTrapEventArgs args) => eventArgs = args); + }); + + await component.Instance._OnMove(5, 6, 7, 8); + + Assert.IsNotNull(eventArgs); + Assert.AreEqual(5, eventArgs!.StartX); + Assert.AreEqual(6, eventArgs.StartY); + Assert.AreEqual(7, eventArgs.DiffX); + Assert.AreEqual(8, eventArgs.DiffY); + } + + [TestMethod] + public async Task BitSwipeTrapShouldInvokeOnEnd() + { + BitSwipeTrapEventArgs? eventArgs = null; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnEnd, (BitSwipeTrapEventArgs args) => eventArgs = args); + }); + + await component.Instance._OnEnd(1, 2, 3, 4); + + Assert.IsNotNull(eventArgs); + Assert.AreEqual(1, eventArgs!.StartX); + Assert.AreEqual(2, eventArgs.StartY); + Assert.AreEqual(3, eventArgs.DiffX); + Assert.AreEqual(4, eventArgs.DiffY); + } + + [TestMethod, + DataRow(10, 2, BitSwipeDirection.Right), + DataRow(-5, 1, BitSwipeDirection.Left), + DataRow(2, 9, BitSwipeDirection.Bottom), + DataRow(3, -7, BitSwipeDirection.Top)] + public async Task BitSwipeTrapShouldInvokeOnTrigger(int diffX, int diffY, BitSwipeDirection expectedDirection) + { + BitSwipeTrapTriggerArgs? triggerArgs = null; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnTrigger, (BitSwipeTrapTriggerArgs args) => triggerArgs = args); + }); + + await component.Instance._OnTrigger(diffX, diffY); + + Assert.IsNotNull(triggerArgs); + Assert.AreEqual(expectedDirection, triggerArgs!.Direction); + Assert.AreEqual(diffX, triggerArgs.DiffX); + Assert.AreEqual(diffY, triggerArgs.DiffY); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs b/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs index eea0bb5368..2d6a35e90a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs @@ -49,7 +49,8 @@ public BitDir? Dir } /// - /// Capture and render additional attributes in addition to the component's parameters. + /// Captures and renders additional attributes in addition to the component's parameters. + /// This parameter should not be assigned directly. /// [Parameter] public Dictionary HtmlAttributes { get; set; } = []; From 6b935a3052afe54b0e105ac1d8e1d55615a83324 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 22 Nov 2025 12:15:41 +0330 Subject: [PATCH 03/39] feat(blazorui): add missing tests for BitCalendar #11714 (#11715) --- .../Inputs/Calendar/BitCalendarTests.cs | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Calendar/BitCalendarTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Calendar/BitCalendarTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Calendar/BitCalendarTests.cs new file mode 100644 index 0000000000..39aee79fcd --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Inputs/Calendar/BitCalendarTests.cs @@ -0,0 +1,103 @@ +using System; +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Inputs.Calendar; + +[TestClass] +public class BitCalendarTests : BunitTestContext +{ + [TestMethod, + DataRow(true), + DataRow(false)] + public void BitCalendarShouldRespectIsEnabled(bool isEnabled) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + }); + + var calendar = component.Find(".bit-cal"); + + if (isEnabled) + { + Assert.IsFalse(calendar.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(calendar.ClassList.Contains("bit-dis")); + } + } + + [TestMethod] + public void BitCalendarShouldRenderDayCellTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.DayCellTemplate, (RenderFragment)(context => + { + RenderFragment fragment = builder => builder.AddContent(0, $"Day-{context.Day}"); + return fragment; + })); + }); + + var firstDayCell = component.Find(".bit-cal-dbt"); + + Assert.IsTrue(firstDayCell.TextContent.Contains("Day-")); + } + + [TestMethod, + DataRow(true), + DataRow(false)] + public void BitCalendarShouldRespectShowWeekNumbers(bool showWeekNumbers) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.ShowWeekNumbers, showWeekNumbers); + }); + + var weekNumbers = component.FindAll(".bit-cal-wnm"); + + if (showWeekNumbers) + { + Assert.IsTrue(weekNumbers.Count > 0); + } + else + { + Assert.AreEqual(0, weekNumbers.Count); + } + } + + [TestMethod] + public void BitCalendarShouldRespectGoToTodayTitle() + { + var title = "Go now"; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.ShowMonthPicker, false); + parameters.Add(p => p.GoToTodayTitle, title); + }); + + var goToTodayButton = component.Find(".bit-cal-gtb"); + + Assert.AreEqual(title, goToTodayButton.GetAttribute("title")); + } + + [TestMethod] + public void BitCalendarSelectingTodayShouldUpdateValue() + { + var component = RenderComponent(); + + Assert.IsNull(component.Instance.Value); + + var todayButton = component.Find(".bit-cal-dtd"); + + todayButton.Click(); + + Assert.IsNotNull(component.Instance.Value); + Assert.AreEqual(DateTimeOffset.Now.Date, component.Instance.Value!.Value.Date); + Assert.AreEqual(TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now), component.Instance.Value!.Value.Offset); + } +} From 14185a73a070d5cf1304b693ba35682a67a81e63 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 22 Nov 2025 13:38:51 +0330 Subject: [PATCH 04/39] feat(blazorui): add missing tests of BitScrollablePane #11716 (#11717) --- .../ScrollablePane/BitScrollablePaneTests.cs | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Surfaces/ScrollablePane/BitScrollablePaneTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Surfaces/ScrollablePane/BitScrollablePaneTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Surfaces/ScrollablePane/BitScrollablePaneTests.cs new file mode 100644 index 0000000000..9f4cf82375 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Surfaces/ScrollablePane/BitScrollablePaneTests.cs @@ -0,0 +1,158 @@ +using System; +using System.Threading.Tasks; +using Bunit; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Surfaces.ScrollablePane; + +[TestClass] +public class BitScrollablePaneTests : BunitTestContext +{ + [TestMethod] + public void BitScrollablePaneShouldRenderChildContent() + { + var component = RenderComponent(parameters => + { + parameters.AddChildContent("

ScrollablePane Content

"); + }); + + component.MarkupMatches(@" +
+

+ ScrollablePane Content +

+
"); + } + + [TestMethod, + DataRow(true), + DataRow(false)] + public void BitScrollablePaneShouldRespectIsEnabled(bool isEnabled) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + }); + + var root = component.Find(".bit-scp"); + + if (isEnabled) + { + Assert.IsFalse(root.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + } + + [TestMethod] + public void BitScrollablePaneShouldInvokeOnScroll() + { + var scrolled = false; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnScroll, () => scrolled = true); + }); + + var root = component.Find(".bit-scp"); + + root.TriggerEvent("onscroll", EventArgs.Empty); + + Assert.IsTrue(scrolled); + } + + [TestMethod] + public void BitScrollablePaneShouldAutoScrollAfterRender() + { + Context.JSInterop.SetupVoid("BitBlazorUI.ScrollablePane.scrollToEnd"); + + RenderComponent(parameters => + { + parameters.Add(p => p.AutoScroll, true); + }); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.ScrollablePane.scrollToEnd"); + } + + [TestMethod] + public async Task BitScrollablePaneScrollToEndShouldCallJs() + { + Context.JSInterop.SetupVoid("BitBlazorUI.ScrollablePane.scrollToEnd"); + + var component = RenderComponent(); + + await component.Instance.ScrollToEnd(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.ScrollablePane.scrollToEnd"); + } + + [TestMethod] + public void BitScrollablePaneShouldRespectOverflowStyles() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Overflow, BitOverflow.Hidden); + }); + + var style = component.Find(".bit-scp").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("overflow:hidden")); + } + + [TestMethod] + public void BitScrollablePaneShouldRespectOverflowXStyles() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OverflowX, BitOverflow.Scroll); + }); + + var style = component.Find(".bit-scp").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("overflow-x:scroll")); + } + + [TestMethod] + public void BitScrollablePaneShouldRespectOverflowYStyles() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OverflowY, BitOverflow.Visible); + }); + + var style = component.Find(".bit-scp").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("overflow-y:visible")); + } + + [TestMethod] + public void BitScrollablePaneShouldRespectOverflowXYStyles() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OverflowX, BitOverflow.Scroll); + parameters.Add(p => p.OverflowY, BitOverflow.Visible); + }); + + var style = component.Find(".bit-scp").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("overflow:scroll visible")); + } + + [TestMethod] + public void BitScrollablePaneShouldRespectDimensions() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Width, "200px"); + parameters.Add(p => p.Height, "120px"); + }); + + var style = component.Find(".bit-scp").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("width:200px")); + Assert.IsTrue(style.Contains("height:120px")); + } +} From a7c23034f1398d95d51b9283b4e9848dc093e732 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 22 Nov 2025 15:17:33 +0330 Subject: [PATCH 05/39] feat(blazorui): add missing tests for BitSwiper #11718 (#11719) --- .../Lists/Swiper/BitSwiperTest.razor | 9 ++ .../Components/Lists/Swiper/BitSwiperTests.cs | 105 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Swiper/BitSwiperTest.razor create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Swiper/BitSwiperTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Swiper/BitSwiperTest.razor b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Swiper/BitSwiperTest.razor new file mode 100644 index 0000000000..ac7492c09a --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Swiper/BitSwiperTest.razor @@ -0,0 +1,9 @@ + +
1
+
2
+
3
+
+ +@code { + [Parameter] public bool HideNextPrev { get; set; } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Swiper/BitSwiperTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Swiper/BitSwiperTests.cs new file mode 100644 index 0000000000..bf99a8f11c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Swiper/BitSwiperTests.cs @@ -0,0 +1,105 @@ +using System.Threading.Tasks; +using Bunit; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Lists.Swiper; + +[TestClass] +public class BitSwiperTests : BunitTestContext +{ + [TestMethod] + public void BitSwiperShouldHideNavigationWhenRequested() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.HideNextPrev, true); + }); + + Assert.AreEqual(0, component.FindAll(".bit-swp-lbt").Count); + Assert.AreEqual(0, component.FindAll(".bit-swp-rbt").Count); + } + + [DataTestMethod, + DataRow(true), + DataRow(false)] + public void BitSwiperShouldRespectIsEnabled(bool isEnabled) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + }); + + var root = component.Find(".bit-swp"); + + if (isEnabled) + { + Assert.IsFalse(root.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + } + + [TestMethod] + public void BitSwiperShouldRespectRtlDirection() + { + Context.JSInterop.SetupVoid("BitBlazorUI.Observers.registerResize"); + Context.JSInterop.SetupVoid("BitBlazorUI.Swiper.setup"); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Dir, BitDir.Rtl); + }); + + var root = component.Find(".bit-swp"); + Assert.IsTrue(root.ClassList.Contains("bit-rtl")); + + var containerStyle = component.Find(".bit-swp-cnt").GetAttribute("style") ?? string.Empty; + Assert.IsTrue(containerStyle.Contains("direction:rtl")); + } + + [TestMethod] + public void BitSwiperShouldRegisterJsInteropOnFirstRender() + { + Context.JSInterop.SetupVoid("BitBlazorUI.Observers.registerResize"); + Context.JSInterop.SetupVoid("BitBlazorUI.Swiper.setup"); + + RenderComponent(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.Observers.registerResize"); + Context.JSInterop.VerifyInvoke("BitBlazorUI.Swiper.setup"); + } + + [TestMethod] + public async Task BitSwiperShouldDisposeJsInteropOnDispose() + { + Context.JSInterop.SetupVoid("BitBlazorUI.Observers.registerResize"); + Context.JSInterop.SetupVoid("BitBlazorUI.Swiper.setup"); + Context.JSInterop.SetupVoid("BitBlazorUI.Swiper.dispose"); + Context.JSInterop.SetupVoid("BitBlazorUI.Observers.unregisterResize"); + + var component = RenderComponent(); + + await component.Instance.DisposeAsync(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.Swiper.dispose"); + Context.JSInterop.VerifyInvoke("BitBlazorUI.Observers.unregisterResize"); + } + + [TestMethod] + public void BitSwiperShouldRenderItems() + { + Context.JSInterop.SetupVoid("BitBlazorUI.Observers.registerResize"); + Context.JSInterop.SetupVoid("BitBlazorUI.Swiper.setup"); + + var component = RenderComponent(); + + var items = component.FindAll(".bit-swpi"); + + Assert.AreEqual(3, items.Count); + + Assert.AreEqual(1, component.FindAll(".bit-swp-lbt").Count); + Assert.AreEqual(1, component.FindAll(".bit-swp-rbt").Count); + } +} From 7a6457bced0e6f72143ac0b0279973aafea1e874 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 22 Nov 2025 15:53:33 +0330 Subject: [PATCH 06/39] feat(blazorui): add missing tests of BitTimeline #11720 (#11721) --- .../Lists/Timeline/BitTimelineTests.cs | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineTests.cs new file mode 100644 index 0000000000..fddc820968 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineTests.cs @@ -0,0 +1,213 @@ +using Bunit; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Lists.Timeline; + +[TestClass] +public class BitTimelineTests : BunitTestContext +{ + [TestMethod] + public void BitTimelineShouldRenderItemsFromChildContent() + { + var component = RenderComponent>(parameters => + { + parameters.AddChildContent(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(BitTimelineOption.PrimaryText), "First"); + builder.AddAttribute(2, nameof(BitTimelineOption.SecondaryText), "First second"); + builder.CloseComponent(); + + builder.OpenComponent(3); + builder.AddAttribute(4, nameof(BitTimelineOption.PrimaryText), "Second"); + builder.AddAttribute(5, nameof(BitTimelineOption.SecondaryText), "Second second"); + builder.CloseComponent(); + }); + }); + + var items = component.FindAll(".bit-tln-itm"); + + Assert.AreEqual(2, items.Count); + + Assert.IsTrue(component.Markup.Contains("First")); + Assert.IsTrue(component.Markup.Contains("Second")); + } + + [DataTestMethod, + DataRow(true), + DataRow(false)] + public void BitTimelineShouldRespectIsEnabled(bool isEnabled) + { + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + parameters.AddChildContent(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(BitTimelineOption.PrimaryText), "First"); + builder.AddAttribute(2, nameof(BitTimelineOption.SecondaryText), "First second"); + builder.CloseComponent(); + + builder.OpenComponent(3); + builder.AddAttribute(4, nameof(BitTimelineOption.PrimaryText), "Second"); + builder.AddAttribute(5, nameof(BitTimelineOption.SecondaryText), "Second second"); + builder.CloseComponent(); + }); + }); + + var root = component.Find(".bit-tln"); + + if (isEnabled) + { + Assert.IsFalse(root.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + } + + [TestMethod] + public void BitTimelineItemClickShouldInvokeCallbackWhenEnabled() + { + BitTimelineOption? clicked = null; + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.OnItemClick, (BitTimelineOption item) => clicked = item); + parameters.AddChildContent(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(BitTimelineOption.PrimaryText), "First"); + builder.AddAttribute(2, nameof(BitTimelineOption.SecondaryText), "First second"); + builder.CloseComponent(); + + builder.OpenComponent(3); + builder.AddAttribute(4, nameof(BitTimelineOption.PrimaryText), "Second"); + builder.AddAttribute(5, nameof(BitTimelineOption.SecondaryText), "Second second"); + builder.CloseComponent(); + }); + }); + + var item = component.Find(".bit-tln-itm"); + + item.Click(); + + Assert.IsNotNull(clicked); + Assert.AreEqual("First", clicked!.PrimaryText); + } + + [TestMethod] + public void BitTimelineDisabledItemShouldNotInvokeCallback() + { + BitTimelineOption? clicked = null; + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.OnItemClick, (BitTimelineOption item) => clicked = item); + parameters.AddChildContent(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(BitTimelineOption.IsEnabled), false); + builder.AddAttribute(2, nameof(BitTimelineOption.PrimaryText), "First"); + builder.AddAttribute(3, nameof(BitTimelineOption.SecondaryText), "First second"); + builder.CloseComponent(); + + builder.OpenComponent(4); + builder.AddAttribute(5, nameof(BitTimelineOption.PrimaryText), "Second"); + builder.AddAttribute(6, nameof(BitTimelineOption.SecondaryText), "Second second"); + builder.CloseComponent(); + }); + }); + + var item = component.Find(".bit-tln-itm"); + + item.Click(); + + Assert.IsNull(clicked); + Assert.IsTrue(item.ClassList.Contains("bit-tln-ids")); + } + + [TestMethod] + public void BitTimelineShouldRenderDotOrHideWhenRequested() + { + var component = RenderComponent>(parameters => + { + parameters.AddChildContent(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(BitTimelineOption.PrimaryText), "ShowDot"); + builder.AddAttribute(2, nameof(BitTimelineOption.SecondaryText), "ShowDot second"); + builder.CloseComponent(); + + builder.OpenComponent(3); + builder.AddAttribute(4, nameof(BitTimelineOption.HideDot), true); + builder.AddAttribute(5, nameof(BitTimelineOption.PrimaryText), "HideDot"); + builder.AddAttribute(6, nameof(BitTimelineOption.SecondaryText), "HideDot second"); + builder.CloseComponent(); + }); + }); + + var dots = component.FindAll(".bit-tln-dot"); + + Assert.AreEqual(1, dots.Count); + } + + [TestMethod] + public void BitTimelineShouldRenderIconWhenProvided() + { + var component = RenderComponent>(parameters => + { + parameters.AddChildContent(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(BitTimelineOption.IconName), "Add"); + builder.AddAttribute(2, nameof(BitTimelineOption.PrimaryText), "First"); + builder.AddAttribute(3, nameof(BitTimelineOption.SecondaryText), "First second"); + builder.CloseComponent(); + + builder.OpenComponent(4); + builder.AddAttribute(5, nameof(BitTimelineOption.PrimaryText), "Second"); + builder.AddAttribute(6, nameof(BitTimelineOption.SecondaryText), "Second second"); + builder.CloseComponent(); + }); + }); + + var icon = component.Find(".bit-tln-ico"); + + Assert.IsTrue(icon.ClassList.Contains("bit-icon--Add")); + } + + [TestMethod] + public void BitTimelineShouldApplyRootClassesFromParameters() + { + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Color, BitColor.Secondary); + parameters.Add(p => p.Size, BitSize.Small); + parameters.Add(p => p.Variant, BitVariant.Outline); + parameters.Add(p => p.Horizontal, true); + parameters.Add(p => p.Reversed, true); + parameters.AddChildContent(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(BitTimelineOption.PrimaryText), "First"); + builder.AddAttribute(2, nameof(BitTimelineOption.SecondaryText), "First second"); + builder.CloseComponent(); + + builder.OpenComponent(3); + builder.AddAttribute(4, nameof(BitTimelineOption.PrimaryText), "Second"); + builder.AddAttribute(5, nameof(BitTimelineOption.SecondaryText), "Second second"); + builder.CloseComponent(); + }); + }); + + var root = component.Find(".bit-tln"); + + Assert.IsTrue(root.ClassList.Contains("bit-tln-sec")); + Assert.IsTrue(root.ClassList.Contains("bit-tln-sm")); + Assert.IsTrue(root.ClassList.Contains("bit-tln-otl")); + Assert.IsTrue(root.ClassList.Contains("bit-tln-hrz")); + Assert.IsTrue(root.ClassList.Contains("bit-tln-rvs")); + } +} From 894853ce0594a16bd8dcb136e761afc784c9a1ad Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 22 Nov 2025 16:01:53 +0330 Subject: [PATCH 07/39] feat(github): update total package downloads #11722 (#11723) --- README.md | 4 ++-- .../Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs | 4 ++-- .../Components/Pages/Home/HomePage.razor | 2 +- .../src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor | 2 +- .../Bit.Websites.Platform.Client/Pages/Lcnc/LcncPage.razor | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d4b96188f2..67f0e204b1 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@
![License](https://img.shields.io/github/license/bitfoundation/bitplatform.svg) -![CI Status](https://github.com/bitfoundation/bitplatform/actions/workflows/bit.ci.yml/badge.svg) +![Release CI Status](https://github.com/bitfoundation/bitplatform/actions/workflows/bit.ci.release.yml/badge.svg) ![NuGet version](https://img.shields.io/nuget/v/bit.blazorui.svg?logo=nuget) -[![Nuget downloads](https://img.shields.io/badge/packages_download-7.9M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) +[![Nuget downloads](https://img.shields.io/badge/packages_download-8.2M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform "Percentage of issues still open") [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bitfoundation/bitplatform) diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs index e88a6c9ae8..5bcd1e5946 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.cs @@ -43,7 +43,7 @@ public partial class BitMarkdownViewerDemo ![License](https://img.shields.io/github/license/bitfoundation/bitplatform.svg) ![CI Status](https://github.com/bitfoundation/bitplatform/actions/workflows/bit.ci.yml/badge.svg) ![NuGet version](https://img.shields.io/nuget/v/bit.blazorui.svg?logo=nuget) -[![Nuget downloads](https://img.shields.io/badge/packages_download-7.9M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) +[![Nuget downloads](https://img.shields.io/badge/packages_download-8.2M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform ""Average time to resolve an issue"") [![Percentage of issues still open](http://isitmaintained.com/badge/open/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform ""Percentage of issues still open"") @@ -152,7 +152,7 @@ private void OnRendered(string? html) ![License](https://img.shields.io/github/license/bitfoundation/bitplatform.svg) ![CI Status](https://github.com/bitfoundation/bitplatform/actions/workflows/bit.ci.yml/badge.svg) ![NuGet version](https://img.shields.io/nuget/v/bit.blazorui.svg?logo=nuget) -[![Nuget downloads](https://img.shields.io/badge/packages_download-7.9M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) +[![Nuget downloads](https://img.shields.io/badge/packages_download-8.2M-blue.svg?logo=nuget)](https://www.nuget.org/profiles/bit-foundation) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform """"Average time to resolve an issue"""") [![Percentage of issues still open](http://isitmaintained.com/badge/open/bitfoundation/bitplatform.svg)](http://isitmaintained.com/project/bitfoundation/bitplatform """"Percentage of issues still open"""") diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Home/HomePage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Home/HomePage.razor index e24fbc0679..1e6330fc6d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Home/HomePage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Home/HomePage.razor @@ -125,7 +125,7 @@ - + diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor index ece95b56ba..d2a560415a 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/AboutUsPage.razor @@ -45,7 +45,7 @@
- 7.9M + 8.2M
diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Lcnc/LcncPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Lcnc/LcncPage.razor index 2bed4b6a81..1d55afdebf 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Lcnc/LcncPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Lcnc/LcncPage.razor @@ -138,7 +138,7 @@
-
+7.9M NuGet downloads
+
+8.2M NuGet downloads
+1K GitHub stars
+100 contributors
From 890299b3f840a655bdf8b38394b9405374a902ba Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 22 Nov 2025 19:04:10 +0330 Subject: [PATCH 08/39] feat(blazorui): add missing tests of loading components #11724 (#11725) --- .../Progress/Loading/BitBarsLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitBouncingDotsLoadingTests.cs | 88 +++++++++++++++++++ .../Progress/Loading/BitCircleLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitDotsRingLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitDualRingLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitEllipsisLoadingTests.cs | 88 +++++++++++++++++++ .../Progress/Loading/BitGridLoadingTests.cs | 88 +++++++++++++++++++ .../Progress/Loading/BitHeartLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitHourglassLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitOrbitingDotsLoadingTests.cs | 88 +++++++++++++++++++ .../Progress/Loading/BitRingLoadingTests.cs | 88 +++++++++++++++++++ .../Progress/Loading/BitRippleLoadingTests.cs | 88 +++++++++++++++++++ .../Progress/Loading/BitRollerLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitRollingDashesLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitRollingSquareLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitSlickBarsLoadingTests.cs | 88 +++++++++++++++++++ .../Loading/BitSpinnerLoadingTests.cs | 88 +++++++++++++++++++ .../Progress/Loading/BitXboxLoadingTests.cs | 88 +++++++++++++++++++ 18 files changed, 1584 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitBarsLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitBouncingDotsLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitCircleLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitDotsRingLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitDualRingLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitEllipsisLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitGridLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitHeartLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitHourglassLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitOrbitingDotsLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRingLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRippleLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollerLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollingDashesLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollingSquareLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitSlickBarsLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitSpinnerLoadingTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitXboxLoadingTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitBarsLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitBarsLoadingTests.cs new file mode 100644 index 0000000000..508e1f2796 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitBarsLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitBarsLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitBarsLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-bar")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-bar-ccn"); + Assert.AreEqual(3, container.GetElementsByClassName("bit-ldn-bar-chl").Length); + } + + [TestMethod] + public void BitBarsLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitBarsLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitBarsLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitBarsLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitBarsLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitBouncingDotsLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitBouncingDotsLoadingTests.cs new file mode 100644 index 0000000000..6e5594536b --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitBouncingDotsLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitBouncingDotsLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitBouncingDotsLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-bnd")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-bnd-ccn"); + Assert.AreEqual(3, container.GetElementsByClassName("bit-ldn-bnd-chl").Length); + } + + [TestMethod] + public void BitBouncingDotsLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitBouncingDotsLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitBouncingDotsLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitBouncingDotsLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitBouncingDotsLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitCircleLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitCircleLoadingTests.cs new file mode 100644 index 0000000000..582c8a0886 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitCircleLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitCircleLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitCircleLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-cir")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-cir-ccn"); + Assert.AreEqual(1, container.GetElementsByClassName("bit-ldn-cir-chl").Length); + } + + [TestMethod] + public void BitCircleLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitCircleLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitCircleLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitCircleLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitCircleLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitDotsRingLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitDotsRingLoadingTests.cs new file mode 100644 index 0000000000..92dbf76f69 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitDotsRingLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitDotsRingLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitDotsRingLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-dor")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-dor-ccn"); + Assert.AreEqual(12, container.GetElementsByClassName("bit-ldn-dor-chl").Length); + } + + [TestMethod] + public void BitDotsRingLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitDotsRingLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitDotsRingLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitDotsRingLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitDotsRingLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitDualRingLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitDualRingLoadingTests.cs new file mode 100644 index 0000000000..853fb9302c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitDualRingLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitDualRingLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitDualRingLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-dur")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-dur-ccn"); + Assert.IsNotNull(container); + } + + [TestMethod] + public void BitDualRingLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitDualRingLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitDualRingLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitDualRingLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitDualRingLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitEllipsisLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitEllipsisLoadingTests.cs new file mode 100644 index 0000000000..14449dd78c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitEllipsisLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitEllipsisLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitEllipsisLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-elp")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-elp-ccn"); + Assert.AreEqual(4, container.GetElementsByClassName("bit-ldn-elp-chl").Length); + } + + [TestMethod] + public void BitEllipsisLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitEllipsisLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitEllipsisLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitEllipsisLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitEllipsisLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitGridLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitGridLoadingTests.cs new file mode 100644 index 0000000000..d225cbe8a5 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitGridLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitGridLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitGridLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-grd")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-grd-ccn"); + Assert.AreEqual(9, container.GetElementsByClassName("bit-ldn-grd-chl").Length); + } + + [TestMethod] + public void BitGridLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitGridLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitGridLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitGridLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitGridLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitHeartLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitHeartLoadingTests.cs new file mode 100644 index 0000000000..df451da234 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitHeartLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitHeartLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitHeartLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-hrt")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-hrt-ccn"); + Assert.AreEqual(1, container.GetElementsByClassName("bit-ldn-hrt-chl").Length); + } + + [TestMethod] + public void BitHeartLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitHeartLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitHeartLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitHeartLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitHeartLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitHourglassLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitHourglassLoadingTests.cs new file mode 100644 index 0000000000..dca4ac5fc2 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitHourglassLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitHourglassLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitHourglassLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-hgl")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-hgl-ccn"); + Assert.IsNotNull(container); + } + + [TestMethod] + public void BitHourglassLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitHourglassLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitHourglassLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitHourglassLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitHourglassLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitOrbitingDotsLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitOrbitingDotsLoadingTests.cs new file mode 100644 index 0000000000..6ad37cba86 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitOrbitingDotsLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitOrbitingDotsLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitOrbitingDotsLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ord")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-ord-ccn"); + Assert.AreEqual(2, container.GetElementsByClassName("bit-ldn-ord-chl").Length); + } + + [TestMethod] + public void BitOrbitingDotsLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitOrbitingDotsLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitOrbitingDotsLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitOrbitingDotsLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitOrbitingDotsLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRingLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRingLoadingTests.cs new file mode 100644 index 0000000000..9011a8eaa5 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRingLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitRingLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitRingLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-rng")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-rng-ccn"); + Assert.AreEqual(4, container.GetElementsByClassName("bit-ldn-rng-chl").Length); + } + + [TestMethod] + public void BitRingLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitRingLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitRingLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitRingLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitRingLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRippleLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRippleLoadingTests.cs new file mode 100644 index 0000000000..0ebc73c328 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRippleLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitRippleLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitRippleLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-rpl")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-rpl-ccn"); + Assert.AreEqual(2, container.GetElementsByClassName("bit-ldn-rpl-chl").Length); + } + + [TestMethod] + public void BitRippleLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitRippleLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitRippleLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitRippleLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitRippleLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollerLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollerLoadingTests.cs new file mode 100644 index 0000000000..aaef3405cf --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollerLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitRollerLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitRollerLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-rol")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-rol-ccn"); + Assert.AreEqual(8, container.GetElementsByClassName("bit-ldn-rol-chl").Length); + } + + [TestMethod] + public void BitRollerLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitRollerLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitRollerLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitRollerLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitRollerLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollingDashesLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollingDashesLoadingTests.cs new file mode 100644 index 0000000000..8d74eb8a74 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollingDashesLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitRollingDashesLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitRollingDashesLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-rld")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-rld-ccn"); + Assert.AreEqual(1, container.GetElementsByClassName("bit-ldn-rld-chl").Length); + } + + [TestMethod] + public void BitRollingDashesLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitRollingDashesLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitRollingDashesLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitRollingDashesLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitRollingDashesLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollingSquareLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollingSquareLoadingTests.cs new file mode 100644 index 0000000000..a7e5855936 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitRollingSquareLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitRollingSquareLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitRollingSquareLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-rsq")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-rsq-ccn"); + Assert.AreEqual(1, container.GetElementsByClassName("bit-ldn-rsq-chl").Length); + } + + [TestMethod] + public void BitRollingSquareLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitRollingSquareLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitRollingSquareLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitRollingSquareLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitRollingSquareLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitSlickBarsLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitSlickBarsLoadingTests.cs new file mode 100644 index 0000000000..ace8a797cd --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitSlickBarsLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitSlickBarsLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitSlickBarsLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-sbr")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-sbr-ccn"); + Assert.AreEqual(6, container.GetElementsByClassName("bit-ldn-sbr-chl").Length); + } + + [TestMethod] + public void BitSlickBarsLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitSlickBarsLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitSlickBarsLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitSlickBarsLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitSlickBarsLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitSpinnerLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitSpinnerLoadingTests.cs new file mode 100644 index 0000000000..527468eb67 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitSpinnerLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitSpinnerLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitSpinnerLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-spn")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-spn-ccn"); + Assert.AreEqual(12, container.GetElementsByClassName("bit-ldn-spn-chl").Length); + } + + [TestMethod] + public void BitSpinnerLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitSpinnerLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitSpinnerLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitSpinnerLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitSpinnerLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitXboxLoadingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitXboxLoadingTests.cs new file mode 100644 index 0000000000..625bdb3dd7 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Progress/Loading/BitXboxLoadingTests.cs @@ -0,0 +1,88 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Progress.Loading; + +[TestClass] +public class BitXboxLoadingTests : BunitTestContext +{ + [TestMethod] + public void BitXboxLoadingShouldRenderStructure() + { + var component = RenderComponent(); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-xbx")); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-ltp")); + + var container = component.Find(".bit-ldn-xbx-ccn"); + Assert.AreEqual(3, container.GetElementsByClassName("bit-ldn-xbx-chl").Length); + } + + [TestMethod] + public void BitXboxLoadingShouldRenderLabel() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Label, "Loading..."); + }); + + var label = component.Find(".bit-ldn-lbl"); + Assert.AreEqual("Loading...", label.TextContent); + } + + [TestMethod] + public void BitXboxLoadingShouldRenderLabelTemplate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelTemplate, (RenderFragment)(b => b.AddMarkupContent(0, "tmpl"))); + }); + + var template = component.Find(".tmpl"); + Assert.AreEqual("tmpl", template.TextContent); + } + + [TestMethod] + public void BitXboxLoadingShouldRespectLabelPosition() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.LabelPosition, BitLabelPosition.End); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("bit-ldn-led")); + } + + [TestMethod] + public void BitXboxLoadingShouldHonorColorAndSize() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Color, BitColor.Warning); + parameters.Add(p => p.Size, BitSize.Small); + }); + + var style = component.Find(".bit-ldn").GetAttribute("style") ?? string.Empty; + + StringAssert.Contains(style, "--bit-ldn-color: var(--bit-clr-wrn)"); + StringAssert.Contains(style, "--bit-ldn-size:40px"); + StringAssert.Contains(style, "--bit-ldn-font-size:10px"); + } + + [TestMethod] + public void BitXboxLoadingShouldRespectRootStyleAndClass() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Class, "custom-root"); + parameters.Add(p => p.Style, "margin:4px;"); + }); + + var root = component.Find(".bit-ldn"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:4px"); + } +} From 402c3546d9975d7498c8717e250b8bd36d69e11d Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 23 Nov 2025 00:00:46 +0330 Subject: [PATCH 09/39] feat(blazorui): add missing BitAppShell tests #11726 (#11727) --- .../Extras/AppShell/BitAppShellTests.cs | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs new file mode 100644 index 0000000000..4f8fef172b --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs @@ -0,0 +1,131 @@ +using System.Reflection; +using Bunit; +using Microsoft.AspNetCore.Components.Routing; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.AppShell; + +[TestClass] +public class BitAppShellTests : BunitTestContext +{ + [TestMethod] + public void BitAppShellShouldRenderStructureAndContent() + { + var component = RenderComponent(parameters => + { + parameters.AddChildContent("
Hello
"); + }); + + var root = component.Find(".bit-ash"); + Assert.IsNotNull(root); + + component.Find(".bit-ash-top"); + component.Find(".bit-ash-center"); + component.Find(".bit-ash-left"); + component.Find(".bit-ash-main"); + component.Find(".bit-ash-right"); + component.Find(".bit-ash-bottom"); + + var content = component.Find(".content"); + + Assert.AreEqual("Hello", content.TextContent); + } + + [TestMethod] + public void BitAppShellShouldRespectClassesAndStyles() + { + var classes = new BitAppShellClassStyles + { + Root = "root-class", + Top = "top-class", + Center = "center-class", + Left = "left-class", + Main = "main-class", + Right = "right-class", + Bottom = "bottom-class" + }; + + var styles = new BitAppShellClassStyles + { + Root = "margin:1px;", + Top = "padding:2px;", + Center = "gap:3px;", + Left = "width:4px;", + Main = "height:5px;", + Right = "border:6px solid transparent;", + Bottom = "background:red;" + }; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Classes, classes); + parameters.Add(p => p.Styles, styles); + }); + + var root = component.Find(".bit-ash"); + + Assert.IsTrue(root.ClassList.Contains("root-class")); + StringAssert.Contains(root.GetAttribute("style") ?? string.Empty, "margin:1px"); + + Assert.IsTrue(component.Find(".bit-ash-top").ClassList.Contains("top-class")); + Assert.IsTrue(component.Find(".bit-ash-center").ClassList.Contains("center-class")); + Assert.IsTrue(component.Find(".bit-ash-left").ClassList.Contains("left-class")); + Assert.IsTrue(component.Find(".bit-ash-main").ClassList.Contains("main-class")); + Assert.IsTrue(component.Find(".bit-ash-right").ClassList.Contains("right-class")); + Assert.IsTrue(component.Find(".bit-ash-bottom").ClassList.Contains("bottom-class")); + + StringAssert.Contains(component.Find(".bit-ash-top").GetAttribute("style") ?? string.Empty, "padding:2px"); + StringAssert.Contains(component.Find(".bit-ash-center").GetAttribute("style") ?? string.Empty, "gap:3px"); + StringAssert.Contains(component.Find(".bit-ash-left").GetAttribute("style") ?? string.Empty, "width:4px"); + StringAssert.Contains(component.Find(".bit-ash-main").GetAttribute("style") ?? string.Empty, "height:5px"); + StringAssert.Contains(component.Find(".bit-ash-right").GetAttribute("style") ?? string.Empty, "border:6px"); + StringAssert.Contains(component.Find(".bit-ash-bottom").GetAttribute("style") ?? string.Empty, "background:red"); + } + + [TestMethod] + public void BitAppShellShouldPersistScroll() + { + Context.JSInterop.SetupVoid("BitBlazorUI.AppShell.initScroll"); + Context.JSInterop.SetupVoid("BitBlazorUI.AppShell.locationChangedScroll"); + Context.JSInterop.SetupVoid("BitBlazorUI.AppShell.afterRenderScroll"); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.PersistScroll, true); + }); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.AppShell.initScroll"); + + InvokeLocationChanged(component.Instance, "https://example.com/page2"); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.AppShell.locationChangedScroll"); + + component.Render(); // trigger OnAfterRenderAsync for non-first render + + Context.JSInterop.VerifyInvoke("BitBlazorUI.AppShell.afterRenderScroll"); + } + + [TestMethod] + public void BitAppShellShouldGoToTopWhenAutoGoToTop() + { + Context.JSInterop.SetupVoid("BitBlazorUI.Extras.goToTop"); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.AutoGoToTop, true); + }); + + InvokeLocationChanged(component.Instance, "https://example.com/other"); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.Extras.goToTop"); + } + + private static void InvokeLocationChanged(BitAppShell instance, string uri) + { + var method = instance.GetType().GetMethod("LocationChanged", BindingFlags.Instance | BindingFlags.NonPublic); + + Assert.IsNotNull(method); + + method!.Invoke(instance, new object?[] { null, new LocationChangedEventArgs(uri, false) }); + } +} From 7a3313a97346405a22592a157265621cd645ad46 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 23 Nov 2025 10:42:42 +0330 Subject: [PATCH 10/39] feta(blazorui): add tests for BitErrorBoundary #11728 (#11729) --- .../ErrorBoundary/BitErrorBoundaryTests.cs | 140 ++++++++++++++++++ .../ErrorBoundary/ThrowOnceComponent.cs | 26 ++++ .../Extras/ErrorBoundary/ThrowingComponent.cs | 15 ++ 3 files changed, 181 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/BitErrorBoundaryTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/ThrowOnceComponent.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/ThrowingComponent.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/BitErrorBoundaryTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/BitErrorBoundaryTests.cs new file mode 100644 index 0000000000..2ab0435fc4 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/BitErrorBoundaryTests.cs @@ -0,0 +1,140 @@ +using System; +using System.Linq; +using Bunit; +using Bunit.TestDoubles; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.ErrorBoundary; + +[TestClass] +public partial class BitErrorBoundaryTests : BunitTestContext +{ + [TestMethod] + public void BitErrorBoundaryShouldRenderChildContentWhenNoError() + { + var component = RenderComponent(parameters => + { + parameters.AddChildContent("
Hello
"); + }); + + var safe = component.Find(".safe"); + + Assert.AreEqual("Hello", safe.TextContent); + Assert.Throws(() => component.Find(".bit-erb")); + } + + [TestMethod] + public void BitErrorBoundaryShouldRenderDefaultErrorAndShowException() + { + var called = false; + var component = RenderComponent(parameters => + { + parameters.Add(p => p.ShowException, true); + parameters.Add(p => p.OnError, EventCallback.Factory.Create(this, _ => called = true)); + parameters.Add(p => p.ChildContent, ThrowingContent("boom")); + }); + + var errorRoot = component.Find(".bit-erb"); + + Assert.IsNotNull(errorRoot); + StringAssert.Contains(errorRoot.TextContent, "Oops, Something went wrong"); + StringAssert.Contains(errorRoot.TextContent, "boom"); + Assert.IsTrue(called); + } + + [TestMethod] + public void BitErrorBoundaryShouldRenderCustomFooter() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Footer, (RenderFragment)(b => b.AddMarkupContent(0, "
Custom
"))); + parameters.Add(p => p.ChildContent, ThrowingContent("err")); + }); + + component.Find(".custom-footer"); + + Assert.Throws(() => component.Find(".bit-erb-ftr")); + } + + [TestMethod] + public void BitErrorBoundaryShouldRenderAdditionalButtons() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.AdditionalButtons, (RenderFragment)(b => b.AddMarkupContent(0, "Extra"))); + parameters.Add(p => p.ChildContent, ThrowingContent("err")); + }); + + component.Find(".extra-btn"); + } + + [TestMethod] + public void BitErrorBoundaryRecoverShouldResetAfterError() + { + ThrowOnceComponent.Reset(); + + var component = RenderComponent(parameters => + { + parameters.AddChildContent(b => + { + b.OpenComponent(0); + b.CloseComponent(); + }); + }); + + component.Find(".bit-erb"); + + var recoverButton = component.FindAll("button").First(btn => btn.TextContent.Contains("Recover", StringComparison.OrdinalIgnoreCase)); + recoverButton.Click(); + + var safe = component.Find(".throw-once-safe"); + Assert.AreEqual("Recovered", safe.TextContent); + } + + [TestMethod] + public void BitErrorBoundaryHomeButtonShouldNavigate() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.HomeUrl, "https://example.com/home"); + parameters.Add(p => p.ChildContent, ThrowingContent("err")); + }); + + var homeButton = component.FindAll("button").First(btn => btn.TextContent.Contains("Home", StringComparison.OrdinalIgnoreCase)); + + homeButton.Click(); + + var navMan = Services.GetRequiredService(); + + Assert.AreEqual("https://example.com/home", navMan.Uri); + } + + [TestMethod] + public void BitErrorBoundaryRefreshButtonShouldInvokeNavigation() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.ChildContent, ThrowingContent("err")); + }); + + var navMan = Services.GetRequiredService(); + + var initialCount = navMan.History.Count; + + var refreshButton = component.FindAll("button").First(btn => btn.TextContent.Contains("Refresh", StringComparison.OrdinalIgnoreCase)); + + refreshButton.Click(); + + Assert.IsTrue(navMan.History.Count > initialCount); + } + + private static RenderFragment ThrowingContent(string message) => b => + { + b.OpenComponent(0); + b.AddAttribute(1, "Message", message); + b.CloseComponent(); + }; +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/ThrowOnceComponent.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/ThrowOnceComponent.cs new file mode 100644 index 0000000000..7ca7262324 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/ThrowOnceComponent.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; + +namespace Bit.BlazorUI.Tests.Components.Extras.ErrorBoundary; + +public class ThrowOnceComponent : ComponentBase +{ + private static int _counter; + + public static void Reset() => _counter = 0; + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (_counter == 0) + { + _counter++; + throw new InvalidOperationException("once"); + } + + builder.OpenElement(0, "div"); + builder.AddAttribute(1, "class", "throw-once-safe"); + builder.AddContent(2, "Recovered"); + builder.CloseElement(); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/ThrowingComponent.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/ThrowingComponent.cs new file mode 100644 index 0000000000..de3979fa07 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ErrorBoundary/ThrowingComponent.cs @@ -0,0 +1,15 @@ +using System; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; + +namespace Bit.BlazorUI.Tests.Components.Extras.ErrorBoundary; + +public class ThrowingComponent : ComponentBase +{ + [Parameter] public string Message { get; set; } = string.Empty; + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + throw new InvalidOperationException(Message); + } +} From 5b0ce8297e357e8f9c62d6bed1c3015ab4045b28 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 23 Nov 2025 12:06:39 +0330 Subject: [PATCH 11/39] feat(blazorui): add unit tests of BitFlag #11730 (#11731) --- .../Components/Extras/Flag/BitFlagTests.cs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/Flag/BitFlagTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/Flag/BitFlagTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/Flag/BitFlagTests.cs new file mode 100644 index 0000000000..3eb840f8be --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/Flag/BitFlagTests.cs @@ -0,0 +1,110 @@ +using Bunit; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.Flag; + +[TestClass] +public class BitFlagTests : BunitTestContext +{ + [TestMethod, + DataRow(true), + DataRow(false)] + public void BitFlagShouldRespectIsEnabled(bool isEnabled) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + }); + + var root = component.Find(".bit-flg"); + + if (isEnabled) + { + Assert.IsFalse(root.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + } + + [TestMethod] + public void BitFlagShouldRespectTitle() + { + const string title = "Netherlands Flag"; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Title, title); + }); + + var root = component.Find(".bit-flg"); + + Assert.AreEqual(title, root.GetAttribute("title")); + } + + [TestMethod] + public void BitFlagShouldRenderFromCountry() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Country, BitCountries.Netherlands); + }); + + var style = component.Find(".bit-flg").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("flags/NL-flat-16.webp")); + } + + [TestMethod] + public void BitFlagShouldRenderFromIso2() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Iso2, "ca"); + }); + + var style = component.Find(".bit-flg").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("flags/CA-flat-16.webp")); + } + + [TestMethod] + public void BitFlagShouldRenderFromIso3() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Iso3, "usa"); + }); + + var style = component.Find(".bit-flg").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("flags/US-flat-16.webp")); + } + + [TestMethod] + public void BitFlagShouldRenderFromCode() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Code, "81"); // Japan + }); + + var style = component.Find(".bit-flg").GetAttribute("style") ?? string.Empty; + + Assert.IsTrue(style.Contains("flags/JP-flat-16.webp")); + } + + [TestMethod] + public void BitFlagShouldRenderEmptyWhenCountryNotFound() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Code, "0000"); + }); + + var style = component.Find(".bit-flg").GetAttribute("style") ?? string.Empty; + + Assert.IsFalse(style.Contains("background-image")); + } +} From ad38c398a843771e580e0220d996fc714be82e1e Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 23 Nov 2025 14:01:08 +0330 Subject: [PATCH 12/39] feat(blazorui): develop unit tests for BitInfiniteScrolling component #11732 (#11733) --- .../BitInfiniteScrollingTests.cs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/InfiniteScrolling/BitInfiniteScrollingTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/InfiniteScrolling/BitInfiniteScrollingTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/InfiniteScrolling/BitInfiniteScrollingTests.cs new file mode 100644 index 0000000000..4e617a6f01 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/InfiniteScrolling/BitInfiniteScrollingTests.cs @@ -0,0 +1,109 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.InfiniteScrolling; + +[TestClass] +public class BitInfiniteScrollingTests : BunitTestContext +{ + private static ValueTask> Provider(BitInfiniteScrollingItemsProviderRequest request) + { + var items = new List(); + + for (var i = request.Skip; i < request.Skip + 3; i++) + { + items.Add(i); + } + + return ValueTask.FromResult>(items); + } + + [TestMethod] + public void BitInfiniteScrollingShouldRenderItemsFromProviderOnPreload() + { + Context.JSInterop.SetupVoid("BitBlazorUI.InfiniteScrolling.setup"); + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.ItemsProvider, Provider); + parameters.Add(p => p.ItemTemplate, (RenderFragment)(item => builder => builder.AddContent(0, $"Item {item}"))); + parameters.Add(p => p.Preload, true); + }); + + component.WaitForAssertion(() => + { + Assert.IsTrue(component.Markup.Contains("Item 0")); + Assert.IsTrue(component.Markup.Contains("Item 1")); + Assert.IsTrue(component.Markup.Contains("Item 2")); + }); + } + + [TestMethod] + public void BitInfiniteScrollingShouldShowEmptyTemplateWhenNoItems() + { + Context.JSInterop.SetupVoid("BitBlazorUI.InfiniteScrolling.setup"); + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.ItemsProvider, emptyProvider); + parameters.Add(p => p.ItemTemplate, (RenderFragment)(item => builder => builder.AddContent(0, $"Item {item}"))); + parameters.Add(p => p.Preload, true); + parameters.Add(p => p.EmptyTemplate, builder => builder.AddContent(0, "No data")); + }); + + component.WaitForAssertion(() => Assert.IsTrue(component.Markup.Contains("No data"))); + + ValueTask> emptyProvider(BitInfiniteScrollingItemsProviderRequest _) + { + return ValueTask.FromResult>(new List()); + } + } + + [TestMethod] + public void BitInfiniteScrollingShouldUseCustomLoadingTemplate() + { + Context.JSInterop.SetupVoid("BitBlazorUI.InfiniteScrolling.setup"); + + var cts = new CancellationTokenSource(); + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.ItemsProvider, slowProvider); + parameters.Add(p => p.ItemTemplate, (RenderFragment)(item => builder => builder.AddContent(0, $"Item {item}"))); + parameters.Add(p => p.LoadingTemplate, builder => builder.AddContent(0, "Custom Loading")); + parameters.Add(p => p.Preload, true); + }); + + component.WaitForAssertion(() => Assert.IsTrue(component.Markup.Contains("Custom Loading"))); + + cts.Cancel(); + + async ValueTask> slowProvider(BitInfiniteScrollingItemsProviderRequest request) + { + await Task.Delay(50, cts.Token); + + return new List(); + } + } + + [TestMethod] + public async Task BitInfiniteScrollingShouldDisposeJsInterop() + { + Context.JSInterop.SetupVoid("BitBlazorUI.InfiniteScrolling.setup"); + Context.JSInterop.SetupVoid("BitBlazorUI.InfiniteScrolling.dispose"); + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.ItemsProvider, Provider); + parameters.Add(p => p.ItemTemplate, (RenderFragment)(item => builder => builder.AddContent(0, $"Item {item}"))); + }); + + await component.Instance.DisposeAsync(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.InfiniteScrolling.dispose"); + } +} From 341ec849800fd362cf022d9830f361605d19d083 Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Sun, 23 Nov 2025 15:51:52 +0330 Subject: [PATCH 13/39] feat(deps): update project dependencies #11736 (#11737) --- .../Bit.BlazorUI.Assets/package-lock.json | 8 ++++---- src/BlazorUI/Bit.BlazorUI.Assets/package.json | 2 +- .../Bit.BlazorUI.Extras/package-lock.json | 8 ++++---- src/BlazorUI/Bit.BlazorUI.Extras/package.json | 2 +- .../Bit.BlazorUI.Icons/package-lock.json | 8 ++++---- src/BlazorUI/Bit.BlazorUI.Icons/package.json | 2 +- src/BlazorUI/Bit.BlazorUI/package-lock.json | 8 ++++---- src/BlazorUI/Bit.BlazorUI/package.json | 2 +- .../Bit.BlazorUI.Demo.Server.csproj | 4 ++-- .../package-lock.json | 8 ++++---- .../Bit.BlazorUI.Demo.Client.Core/package.json | 2 +- .../Bit.ResxTranslator/Bit.ResxTranslator.csproj | 4 ++-- .../Boilerplate.Client.Core/package-lock.json | 8 ++++---- .../Client/Boilerplate.Client.Core/package.json | 2 +- .../Bit.Boilerplate/src/Directory.Packages.props | 16 ++++++++-------- .../package-lock.json | 8 ++++---- .../src/Bit.Websites.Careers.Client/package.json | 2 +- .../package-lock.json | 8 ++++---- .../Bit.Websites.Platform.Client/package.json | 2 +- .../Bit.Websites.Platform.Server.csproj | 4 ++-- .../Bit.Websites.Sales.Client/package-lock.json | 8 ++++---- .../src/Bit.Websites.Sales.Client/package.json | 2 +- 22 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json index cf4ef0622b..55e16c9fc8 100644 --- a/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "devDependencies": { - "sass": "1.94.0" + "sass": "1.94.2" } }, "node_modules/@parcel/watcher": { @@ -448,9 +448,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/BlazorUI/Bit.BlazorUI.Assets/package.json b/src/BlazorUI/Bit.BlazorUI.Assets/package.json index 3b5c473669..770c7c81bd 100644 --- a/src/BlazorUI/Bit.BlazorUI.Assets/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Assets/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "sass": "1.94.0" + "sass": "1.94.2" } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json index 72e69cb7b5..761be99814 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } }, @@ -934,9 +934,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/package.json b/src/BlazorUI/Bit.BlazorUI.Extras/package.json index 1c1766e6ef..31cf33cc63 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Extras/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } } diff --git a/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json index 57ce75273d..741f3ff1bf 100644 --- a/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "devDependencies": { - "sass": "1.94.0" + "sass": "1.94.2" } }, "node_modules/@parcel/watcher": { @@ -448,9 +448,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/BlazorUI/Bit.BlazorUI.Icons/package.json b/src/BlazorUI/Bit.BlazorUI.Icons/package.json index 3b5c473669..770c7c81bd 100644 --- a/src/BlazorUI/Bit.BlazorUI.Icons/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Icons/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "sass": "1.94.0" + "sass": "1.94.2" } } diff --git a/src/BlazorUI/Bit.BlazorUI/package-lock.json b/src/BlazorUI/Bit.BlazorUI/package-lock.json index 9ef6936761..6281ce35a5 100644 --- a/src/BlazorUI/Bit.BlazorUI/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } }, @@ -934,9 +934,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/BlazorUI/Bit.BlazorUI/package.json b/src/BlazorUI/Bit.BlazorUI/package.json index 1c1766e6ef..31cf33cc63 100644 --- a/src/BlazorUI/Bit.BlazorUI/package.json +++ b/src/BlazorUI/Bit.BlazorUI/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } } diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj index 342c224170..69f3cf4fc8 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj @@ -21,7 +21,7 @@ - + @@ -30,7 +30,7 @@ - + diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json index a0b4b5c1c8..0133cafaf0 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } }, @@ -934,9 +934,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json index 1c1766e6ef..31cf33cc63 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } } diff --git a/src/ResxTranslator/Bit.ResxTranslator/Bit.ResxTranslator.csproj b/src/ResxTranslator/Bit.ResxTranslator/Bit.ResxTranslator.csproj index a902fddd57..cb7046c22b 100644 --- a/src/ResxTranslator/Bit.ResxTranslator/Bit.ResxTranslator.csproj +++ b/src/ResxTranslator/Bit.ResxTranslator/Bit.ResxTranslator.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json index 54fe876cd2..0533e715b4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } }, @@ -934,9 +934,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json index 1c1766e6ef..31cf33cc63 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props index 94f8cf3cbc..fc3a652a61 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props @@ -57,16 +57,16 @@ - + - + - + - + @@ -85,8 +85,8 @@ - - + + @@ -96,7 +96,7 @@ - + @@ -123,7 +123,7 @@ - + diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json index 317d1ad3bf..6ad42776fa 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } }, @@ -934,9 +934,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json index 1c1766e6ef..31cf33cc63 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } } diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json index 65d3bdd76d..b0aaff8b86 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } }, @@ -934,9 +934,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json index 1c1766e6ef..31cf33cc63 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } } diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj index bfa29839f5..56df9d3cea 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj @@ -21,11 +21,11 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json index 46e701468a..478fc003d9 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } }, @@ -934,9 +934,9 @@ } }, "node_modules/sass": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", - "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json index 1c1766e6ef..31cf33cc63 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.27.0", - "sass": "1.94.0", + "sass": "1.94.2", "typescript": "5.9.3" } } From c21f35585fc3547e948df8d9bbe7f3ca6c2c0d96 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 23 Nov 2025 15:54:51 +0330 Subject: [PATCH 14/39] feat(blazorui): implement BitMarkdownEditor tests #11734 (#11735) --- .../MarkdownEditor/BitMarkdownEditorTests.cs | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MarkdownEditor/BitMarkdownEditorTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MarkdownEditor/BitMarkdownEditorTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MarkdownEditor/BitMarkdownEditorTests.cs new file mode 100644 index 0000000000..a018208ac2 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MarkdownEditor/BitMarkdownEditorTests.cs @@ -0,0 +1,141 @@ +using System.Threading.Tasks; +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.MarkdownEditor; + +[TestClass] +public class BitMarkdownEditorTests : BunitTestContext +{ + [TestMethod, + DataRow(true), + DataRow(false)] + public void BitMarkdownEditorShouldRespectIsEnabled(bool isEnabled) + { + Context.JSInterop.SetupVoid("BitBlazorUI.MarkdownEditor.init"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.setValue"); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + }); + + var root = component.Find(".bit-mde"); + + if (isEnabled) + { + Assert.IsFalse(root.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + } + + [TestMethod] + public void BitMarkdownEditorShouldInitializeWithDefaultValue() + { + Context.JSInterop.SetupVoid("BitBlazorUI.MarkdownEditor.init"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.setValue"); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Id, "id"); + parameters.Add(p => p.DefaultValue, "hello"); + }); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.MarkdownEditor.init"); + } + + [TestMethod] + public async Task BitMarkdownEditorShouldInvokeOnChange() + { + string? changed = null; + + Context.JSInterop.SetupVoid("BitBlazorUI.MarkdownEditor.init"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.setValue"); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnChange, EventCallback.Factory.Create(this, (string? value) => + { + changed = value; + return Task.CompletedTask; + })); + }); + + await component.Instance._OnChange("new value"); + + Assert.AreEqual("new value", changed); + Assert.AreEqual("new value", component.Instance.Value); + } + + [TestMethod] + public async Task BitMarkdownEditorShouldSetValueAndCallJs() + { + Context.JSInterop.SetupVoid("BitBlazorUI.MarkdownEditor.init"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.setValue"); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Value, "initial"); + }); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.MarkdownEditor.setValue", 1); + + Assert.AreEqual("initial", component.Instance.Value); + + component.SetParametersAndRender(parameters => + { + parameters.Add(p => p.Value, "updated"); + }); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.MarkdownEditor.setValue", 2); + + Assert.AreEqual("updated", component.Instance.Value); + } + + [TestMethod] + public async Task BitMarkdownEditorShouldRunCommand() + { + Context.JSInterop.SetupVoid("BitBlazorUI.MarkdownEditor.init"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.setValue"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.run"); + + var component = RenderComponent(); + + await component.Instance.Run(BitMarkdownEditorCommand.Bold); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.MarkdownEditor.run"); + } + + [TestMethod] + public async Task BitMarkdownEditorShouldAddContent() + { + Context.JSInterop.SetupVoid("BitBlazorUI.MarkdownEditor.init"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.setValue"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.add"); + + var component = RenderComponent(); + + await component.Instance.Add("block", BitMarkdownEditorContentType.Block); + await component.Instance.Add("inline", BitMarkdownEditorContentType.Inline); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.MarkdownEditor.add", 2); + } + + [TestMethod] + public async Task BitMarkdownEditorShouldDisposeJsInterop() + { + Context.JSInterop.SetupVoid("BitBlazorUI.MarkdownEditor.init"); + Context.JSInterop.SetupVoid("BitBlazorUI.MarkdownEditor.dispose"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownEditor.setValue"); + + var component = RenderComponent(); + + await component.Instance.DisposeAsync(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.MarkdownEditor.dispose"); + } +} From 68989c57303858cc406ddb30e88b86af7222abbc Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Sun, 23 Nov 2025 18:29:26 +0330 Subject: [PATCH 15/39] feat(templates): AppComponentBase now inherits from OwningComponentBase #11738 (#11739) --- .../Components/AppComponentBase.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs index 8487cbc34b..ad210260bf 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs @@ -2,7 +2,7 @@ namespace Boilerplate.Client.Core.Components; -public partial class AppComponentBase : ComponentBase, IAsyncDisposable +public partial class AppComponentBase : OwningComponentBase, IAsyncDisposable { /// /// @@ -260,16 +260,21 @@ protected async Task Abort() public async ValueTask DisposeAsync() { - if (cts != null) + try { - using var currentCts = cts; - cts = null; - await currentCts.TryCancel(); - } - - await DisposeAsync(true); + if (cts != null) + { + using var currentCts = cts; + cts = null; + await currentCts.TryCancel(); + } - GC.SuppressFinalize(this); + await DisposeAsync(true); + } + finally + { + await DisposeAsyncCore(); // Would dispose OwiningComponentBase's ScopedServices + } } protected virtual ValueTask DisposeAsync(bool disposing) From f72cf7ec4cb5ae46f19b4e75ff623ab82049f162 Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Sun, 23 Nov 2025 18:51:23 +0330 Subject: [PATCH 16/39] feat(infra): use MsTestRunner instead of VsTestRunner in bitplatform test projects #11743 (#11744) --- src/Bit.slnx | 24 +++++++++---------- .../Bit.BlazorUI.Tests.csproj | 1 + .../Boilerplate/Bit.Boilerplate/global.json | 3 +++ .../src/Tests/Boilerplate.Tests.csproj | 1 + src/global.json | 3 +++ 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Bit.slnx b/src/Bit.slnx index 164656d65b..edbadc6304 100644 --- a/src/Bit.slnx +++ b/src/Bit.slnx @@ -6,18 +6,6 @@ - - - - - - - - - - - - @@ -30,6 +18,18 @@ + + + + + + + + + + + + diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj b/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj index dafc83580c..44b12758e1 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj @@ -8,6 +8,7 @@ 14.0 enable Major + true diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/global.json b/src/Templates/Boilerplate/Bit.Boilerplate/global.json index 6ce09c7c74..48af5972b2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/global.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/global.json @@ -2,5 +2,8 @@ "sdk": { "version": "10.0.100", "rollForward": "latestFeature" + }, + "test": { + "runner": "Microsoft.Testing.Platform" } } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Boilerplate.Tests.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Boilerplate.Tests.csproj index 1ecbdf93b8..4ce9df2300 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Boilerplate.Tests.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Boilerplate.Tests.csproj @@ -5,6 +5,7 @@ enable $(MSBuildProjectDirectory)\.runsettings --settings ./.runsettings + true diff --git a/src/global.json b/src/global.json index b7bd4121a2..aa85424640 100644 --- a/src/global.json +++ b/src/global.json @@ -2,5 +2,8 @@ "sdk": { "version": "10.0.100", "rollForward": "disable" + }, + "test": { + "runner": "Microsoft.Testing.Platform" } } \ No newline at end of file From f0b8678c83d6ee438add616f141f85dd4b7a380c Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Sun, 23 Nov 2025 20:02:23 +0330 Subject: [PATCH 17/39] feat(templates): improve bit Boilerplate test project configuration #11745 (#11746) --- .../Bit.Boilerplate/src/Tests/.runsettings | 16 ++-------------- .../Bit.Boilerplate/src/Tests/MSTestSettings.cs | 1 + 2 files changed, 3 insertions(+), 14 deletions(-) create mode 100644 src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/MSTestSettings.cs diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/.runsettings b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/.runsettings index e57e6defb4..a3af6a7c0d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/.runsettings +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/.runsettings @@ -1,27 +1,15 @@ - - - - - - - - - - - 3 - MethodLevel - - true + + Data Source=BoilerplateDb.db;Mode=Memory;Cache=Shared; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/MSTestSettings.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/MSTestSettings.cs new file mode 100644 index 0000000000..341374ed30 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/MSTestSettings.cs @@ -0,0 +1 @@ +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 3)] From 8e37940bb7b589a846ccbc7302449b7d3c2a1676 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Tue, 25 Nov 2025 11:39:34 +0330 Subject: [PATCH 18/39] feat(blazorui): add missing tests of BitMarkdownViewer #11740 (#11742) --- .../MarkdownViewer/BitMarkdownViewerTests.cs | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MarkdownViewer/BitMarkdownViewerTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MarkdownViewer/BitMarkdownViewerTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MarkdownViewer/BitMarkdownViewerTests.cs new file mode 100644 index 0000000000..502399b1cb --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MarkdownViewer/BitMarkdownViewerTests.cs @@ -0,0 +1,153 @@ +using System.Threading.Tasks; +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.MarkdownViewer; + +[TestClass] +public class BitMarkdownViewerTests : BunitTestContext +{ + private const string MARKED_FILE = "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js"; + + [TestInitialize] + public void RegisterService() + { + Services.AddSingleton(); + } + + private void SetupMarkdownInterop(string markdown, string html) + { + Context.JSInterop.SetupVoid("BitBlazorUI.Extras.initScripts"); + Context.JSInterop.Setup("BitBlazorUI.MarkdownViewer.parse", markdown).SetResult(html); + Context.JSInterop.Setup("BitBlazorUI.MarkdownViewer.parseAsync", markdown).SetResult(html); + Context.JSInterop.Setup("BitBlazorUI.MarkdownViewer.checkScriptLoaded", MARKED_FILE).SetResult(true); + } + + [TestMethod] + public void BitMarkdownViewerShouldRenderParsedHtml() + { + var markdown = "hello"; + var html = "

hello

"; + + SetupMarkdownInterop(markdown, html); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Markdown, markdown); + }); + + var root = component.Find(".bit-mdv"); + + component.WaitForAssertion(() => + { + Assert.IsTrue(root.InnerHtml.Contains(html)); + Assert.AreEqual(Context.JSInterop.VerifyInvoke("BitBlazorUI.MarkdownViewer.parseAsync").Arguments[0], markdown); + }); + } + + [TestMethod] + public void BitMarkdownViewerShouldInvokeCallbacks() + { + var markdown = "callbacks"; + var html = "

callbacks

"; + + SetupMarkdownInterop(markdown, html); + + bool parsingCalled = false, parsedCalled = false, renderedCalled = false; + string? parsedValue = null; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Markdown, markdown); + + parameters.Add(p => p.OnParsing, EventCallback.Factory.Create(this, (string? value) => + { + parsingCalled = value == markdown; + return Task.CompletedTask; + })); + + parameters.Add(p => p.OnParsed, EventCallback.Factory.Create(this, (string? parsed) => + { + parsedCalled = parsed == html; + parsedValue = parsed; + return Task.CompletedTask; + })); + + parameters.Add(p => p.OnRendered, EventCallback.Factory.Create(this, (string? parsed) => + { + renderedCalled = parsed == parsedValue; + return Task.CompletedTask; + })); + }); + + component.WaitForAssertion(() => + { + Assert.IsTrue(parsingCalled); + Assert.IsTrue(parsedCalled); + Assert.IsTrue(renderedCalled); + }); + } + + [TestMethod] + public void BitMarkdownViewerShouldReparseWhenMarkdownChanges() + { + var markdown = "one"; + var html = "

one

"; + + SetupMarkdownInterop(markdown, html); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Markdown, markdown); + }); + + component.WaitForAssertion(() => + { + Assert.IsTrue(component.Markup.Contains(html)); + }); + + markdown = "two"; + html = "

two

"; + + SetupMarkdownInterop(markdown, html); + + component.SetParametersAndRender(parameters => + { + parameters.Add(p => p.Markdown, markdown); + }); + + component.WaitForAssertion(() => + { + Assert.IsTrue(component.Markup.Contains(html)); + }); + } + + [DataTestMethod, + DataRow(true), + DataRow(false)] + public void BitMarkdownViewerShouldRespectIsEnabled(bool isEnabled) + { + var markdown = "enable"; + var html = "

enable

"; + + SetupMarkdownInterop(markdown, html); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + }); + + var root = component.Find(".bit-mdv"); + + if (isEnabled) + { + Assert.IsFalse(root.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + } +} From 499781a031db392c04691217c3ef98a430138410 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Tue, 25 Nov 2025 12:36:02 +0330 Subject: [PATCH 19/39] feat(blazorui): add missing units tests of BitMessageBox #11748 (#11749) --- .../Extras/MessageBox/BitMessageBoxTests.cs | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MessageBox/BitMessageBoxTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MessageBox/BitMessageBoxTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MessageBox/BitMessageBoxTests.cs new file mode 100644 index 0000000000..ae9956f0f1 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/MessageBox/BitMessageBoxTests.cs @@ -0,0 +1,93 @@ +using Bunit; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.MessageBox; + +[TestClass] +public class BitMessageBoxTests : BunitTestContext +{ + [TestMethod] + public void BitMessageBoxShouldRenderTitleBodyAndDefaultOk() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Title, "Sample Title"); + parameters.Add(p => p.Body, "Sample Body"); + }); + + Assert.IsTrue(component.Markup.Contains("Sample Title")); + Assert.IsTrue(component.Markup.Contains("Sample Body")); + + var okButtonText = component.Find(".bit-msb-ftr .bit-btn-prt"); + Assert.AreEqual("Ok", okButtonText.TextContent); + } + + [TestMethod] + public void BitMessageBoxShouldRenderCustomOkText() + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OkText, "Confirm"); + }); + + var okButton = component.Find(".bit-msb-ftr .bit-btn-prt"); + + Assert.AreEqual("Confirm", okButton.TextContent); + } + + [TestMethod] + public void BitMessageBoxShouldInvokeOnCloseFromCloseButton() + { + var closed = 0; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnClose, () => closed++); + }); + + var closeButton = component.Find(".bit-msb-hdr .bit-btn"); + + closeButton.Click(); + + Assert.AreEqual(1, closed); + } + + [TestMethod] + public void BitMessageBoxShouldInvokeOnCloseFromOkButton() + { + var closed = 0; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnClose, () => closed++); + }); + + var okButton = component.Find(".bit-msb-ftr .bit-btn"); + + okButton.Click(); + + Assert.AreEqual(1, closed); + } + + [TestMethod, + DataRow(true), + DataRow(false)] + public void BitMessageBoxShouldRespectIsEnabled(bool isEnabled) + { + var component = RenderComponent(parameters => + { + parameters.Add(p => p.IsEnabled, isEnabled); + }); + + var root = component.Find(".bit-msb"); + + if (isEnabled) + { + Assert.IsFalse(root.ClassList.Contains("bit-dis")); + } + else + { + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + } +} From 5aaca72b952fdd55160a39686cd4a8f0c484b617 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Tue, 25 Nov 2025 13:43:43 +0330 Subject: [PATCH 20/39] feat(blazorui): develop unit tests for BitModalService #11750 (#11751) --- .../ModalService/BitModalServiceTests.cs | 73 +++++++++++++++++++ .../Extras/ModalService/TestModalContent.cs | 17 +++++ 2 files changed, 90 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ModalService/BitModalServiceTests.cs create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ModalService/TestModalContent.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ModalService/BitModalServiceTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ModalService/BitModalServiceTests.cs new file mode 100644 index 0000000000..c8eb42c373 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ModalService/BitModalServiceTests.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Bunit; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.ModalService; + +[TestClass] +public class BitModalServiceTests : BunitTestContext +{ + private BitModalService ModalService => Services.GetRequiredService(); + + [TestInitialize] + public void SetupServices() + { + Services.AddSingleton(); + } + + [TestMethod] + public async Task BitModalServiceShouldRenderModalInContainer() + { + var message = "Hello modal"; + + var container = RenderComponent(); + + await ModalService.Show(new Dictionary + { + { nameof(TestModalContent.Message), message } + }); + + container.WaitForAssertion(() => + { + Assert.AreEqual(1, container.FindAll(".bit-mdl").Count); + Assert.IsTrue(container.Markup.Contains(message)); + }); + } + + [TestMethod] + public async Task BitModalServiceShouldCloseModal() + { + var container = RenderComponent(); + + var modalRef = await ModalService.Show(); + + container.WaitForAssertion(() => + { + Assert.AreEqual(1, container.FindAll(".bit-mdl").Count); + }); + + modalRef.Close(); + + container.WaitForAssertion(() => + { + Assert.AreEqual(0, container.FindAll(".bit-mdl").Count); + }); + } + + [TestMethod] + public async Task BitModalServiceShouldRenderPersistentModalAfterContainerInit() + { + var modalRef = await ModalService.Show(persistent: true); + + var container = RenderComponent(); + + container.WaitForAssertion(() => + { + Assert.AreEqual(1, container.FindAll(".bit-mdl").Count); + }); + + Assert.IsTrue(modalRef.Persistent); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ModalService/TestModalContent.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ModalService/TestModalContent.cs new file mode 100644 index 0000000000..0f6560db36 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ModalService/TestModalContent.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; + +namespace Bit.BlazorUI.Tests.Components.Extras.ModalService; + +public class TestModalContent : ComponentBase +{ + [Parameter] public string Message { get; set; } = string.Empty; + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.OpenElement(0, "p"); + builder.AddAttribute(1, "class", "test-modal-content"); + builder.AddContent(2, Message); + builder.CloseElement(); + } +} From a164e7a842a4744f55307cd0369a4cdc99bae48e Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Tue, 25 Nov 2025 15:19:25 +0330 Subject: [PATCH 21/39] feat(blazorui): add tests for BitNavPanel #11752 (#11753) --- .../Extras/NavPanel/BitNavPanelTests.cs | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/NavPanel/BitNavPanelTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/NavPanel/BitNavPanelTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/NavPanel/BitNavPanelTests.cs new file mode 100644 index 0000000000..b313267fd9 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/NavPanel/BitNavPanelTests.cs @@ -0,0 +1,193 @@ +using System.Collections.Generic; +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.NavPanel; + +[TestClass] +public class BitNavPanelTests : BunitTestContext +{ + private static readonly IList Items = + [ + new() { Text = "Home", Url = "/home", Description = "Home page" }, + new() { Text = "Docs", Url = "/docs", Description = "Documentation" } + ]; + + [TestMethod] + public void BitNavPanelShouldRenderHeaderFooterAndItems() + { + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Add(p => p.Header, b => b.AddMarkupContent(0, "
Header
")); + parameters.Add(p => p.Footer, b => b.AddMarkupContent(0, "
Footer
")); + }); + + var root = component.Find(".bit-npn"); + var header = component.Find(".hdr"); + var footer = component.Find(".ftr"); + + Assert.IsNotNull(root); + Assert.AreEqual("Header", header.TextContent); + Assert.AreEqual("Footer", footer.TextContent); + Assert.AreEqual(2, component.FindAll(".bit-nav-ict").Count); + } + + [TestMethod] + public void BitNavPanelToggleButtonShouldToggleState() + { + var isToggled = false; + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Bind(p => p.IsToggled, isToggled, v => isToggled = v); + }); + + var toggleBtn = component.Find(".bit-npn-tbn"); + + toggleBtn.Click(); + + component.WaitForAssertion(() => + { + Assert.IsTrue(isToggled); + Assert.IsTrue(component.Find(".bit-npn").ClassList.Contains("bit-npn-tgl")); + }); + } + + [TestMethod] + public void BitNavPanelOverlayClickShouldClose() + { + var isOpen = true; + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Bind(p => p.IsOpen, isOpen, v => isOpen = v); + }); + + var overlay = component.Find(".bit-npn-ovl"); + + overlay.Click(); + + component.WaitForAssertion(() => + { + Assert.IsFalse(isOpen); + Assert.IsTrue(component.Find(".bit-npn").ClassList.Contains("bit-npn-cls")); + }); + } + + [TestMethod] + public void BitNavPanelItemClickShouldInvokeAndCloseWhenUrlExists() + { + var isOpen = true; + string? clicked = null; + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Bind(p => p.IsOpen, isOpen, v => isOpen = v); + parameters.Add(p => p.OnItemClick, (BitNavItem item) => clicked = item.Text); + }); + + var firstLink = component.Find(".bit-nav-ict"); + + firstLink.Click(); + + component.WaitForAssertion(() => + { + Assert.AreEqual("Home", clicked); + Assert.IsFalse(isOpen); + }); + } + + [TestMethod] + public void BitNavPanelShouldHideToggleWhenRequested() + { + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Add(p => p.HideToggle, true); + }); + + Assert.AreEqual(0, component.FindAll(".bit-npn-tbn").Count); + } + + [TestMethod] + public void BitNavPanelNoToggleShouldIgnoreToggledState() + { + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Add(p => p.NoToggle, true); + parameters.Add(p => p.IsToggled, true); + }); + + Assert.AreEqual(0, component.FindAll(".bit-npn-tbn").Count); + Assert.IsFalse(component.Find(".bit-npn").ClassList.Contains("bit-npn-tgl")); + } + + [TestMethod] + public void BitNavPanelShouldRespectLayoutClassesAndTopStyle() + { + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Add(p => p.FitWidth, true); + parameters.Add(p => p.FullWidth, true); + parameters.Add(p => p.NoPad, true); + parameters.Add(p => p.Top, 24); + }); + + var root = component.Find(".bit-npn"); + + Assert.IsTrue(root.ClassList.Contains("bit-npn-fiw")); + Assert.IsTrue(root.ClassList.Contains("bit-npn-fuw")); + Assert.IsTrue(root.ClassList.Contains("bit-npn-npd")); + Assert.IsTrue(root.GetAttribute("style")?.Contains("top:24px")); + } + + [TestMethod] + public void BitNavPanelShouldRenderIconWithNavigation() + { + var url = "https://example.com"; + + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Add(p => p.IconUrl, "/logo.png"); + parameters.Add(p => p.IconNavUrl, url); + }); + + var img = component.Find("img.bit-npn-img"); + var a = component.Find("a:has(img.bit-npn-img)"); + + Assert.AreEqual("/logo.png", img.GetAttribute("src")); + Assert.AreEqual(a.GetAttribute("href"), url); + } + + [TestMethod] + public void BitNavPanelShouldHideSearchWhenRequested() + { + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + parameters.Add(p => p.NoSearchBox, true); + }); + + Assert.AreEqual(0, component.FindAll(".bit-srb-inp").Count); + Assert.AreEqual(0, component.FindAll(".bit-npn-tsb").Count); + } + + [TestMethod] + public void BitNavPanelShouldRenderSearchBoxByDefault() + { + var component = RenderComponent>(parameters => + { + parameters.Add(p => p.Items, Items); + }); + + Assert.AreEqual(1, component.FindAll(".bit-srb-inp").Count); + } +} From a3f0b5b96fed5f25fc12f6c29129a18abffa5964 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Wed, 26 Nov 2025 14:42:21 +0330 Subject: [PATCH 22/39] feat(blazorui): add tests of BitPdfReader #11754 (#11755) --- .../Extras/PdfReader/BitPdfReaderTests.cs | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/PdfReader/BitPdfReaderTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/PdfReader/BitPdfReaderTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/PdfReader/BitPdfReaderTests.cs new file mode 100644 index 0000000000..6d6ea8d960 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/PdfReader/BitPdfReaderTests.cs @@ -0,0 +1,160 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.PdfReader; + +[TestClass] +public class BitPdfReaderTests : BunitTestContext +{ + private const string PdfId = "pdf-test"; + + private void SetupJSInterop(int pagesCount = 3) + { + Context.JSInterop.SetupVoid("BitBlazorUI.Extras.initScripts"); + Context.JSInterop.SetupVoid("BitBlazorUI.PdfReader.renderPage"); + Context.JSInterop.SetupVoid("BitBlazorUI.PdfReader.refreshPage"); + Context.JSInterop.SetupVoid("BitBlazorUI.PdfReader.dispose"); + + Context.JSInterop.Setup("BitBlazorUI.PdfReader.setup", inv => inv.Identifier == "BitBlazorUI.PdfReader.setup").SetResult(pagesCount); + } + + [TestMethod] + public void BitPdfReaderShouldRenderSingleCanvasWhenRenderAllPagesIsFalse() + { + SetupJSInterop(3); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Config, new BitPdfReaderConfig { Id = PdfId }); + parameters.Add(p => p.RenderAllPages, false); + }); + + var canvas = component.Find("canvas"); + + var renders = Context.JSInterop.Invocations.Where(i => i.Identifier == "BitBlazorUI.PdfReader.renderPage").ToList(); + + Assert.AreEqual(PdfId, canvas.Id); + Assert.AreEqual(1, renders.Count); + Assert.AreEqual(1, (int)renders[0].Arguments[1]!); + } + + [TestMethod] + public void BitPdfReaderShouldRenderAllPagesWhenEnabled() + { + SetupJSInterop(4); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Config, new BitPdfReaderConfig { Id = PdfId }); + parameters.Add(p => p.RenderAllPages, true); + }); + + var canvases = component.FindAll("canvas"); + + var renders = Context.JSInterop.Invocations.Where(i => i.Identifier == "BitBlazorUI.PdfReader.renderPage").ToList(); + + Assert.AreEqual(4, renders.Count); + Assert.AreEqual(4, canvases.Count); + Assert.IsTrue(canvases.Any(c => c.Id == $"{PdfId}-1")); + Assert.IsTrue(canvases.Any(c => c.Id == $"{PdfId}-4")); + } + + [TestMethod] + public void BitPdfReaderShouldInvokeLoadedAndPageRenderedCallbacks() + { + SetupJSInterop(2); + + var loaded = false; + var rendered = false; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Config, new BitPdfReaderConfig { Id = PdfId }); + parameters.Add(p => p.OnPdfLoaded, EventCallback.Factory.Create(this, () => loaded = true)); + parameters.Add(p => p.OnPdfPageRendered, EventCallback.Factory.Create(this, () => rendered = true)); + }); + + Assert.IsTrue(loaded); + Assert.IsTrue(rendered); + } + + [TestMethod] + public async Task BitPdfReaderNavigationShouldRenderCorrectPages() + { + SetupJSInterop(3); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Config, new BitPdfReaderConfig { Id = PdfId }); + }); + + await component.Instance.Next(); + await component.Instance.Prev(); + await component.Instance.Last(); + await component.Instance.First(); + + var renders = Context.JSInterop.Invocations.Where(i => i.Identifier == "BitBlazorUI.PdfReader.renderPage") + .Select(i => (int)i.Arguments[1]!) + .ToList(); + + CollectionAssert.AreEqual(new List { 1, 2, 1, 3, 1 }, renders); + } + + [TestMethod] + public async Task BitPdfReaderRefreshAllShouldCallJsForEachPage() + { + SetupJSInterop(3); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Config, new BitPdfReaderConfig { Id = PdfId }); + parameters.Add(p => p.RenderAllPages, true); + }); + + await component.Instance.RefreshAll(); + + var refreshes = Context.JSInterop.Invocations.Where(i => i.Identifier == "BitBlazorUI.PdfReader.refreshPage").ToList(); + + Assert.AreEqual(3, refreshes.Count); + } + + [TestMethod] + public void BitPdfReaderShouldRespectOrientationAndEnabledState() + { + SetupJSInterop(); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Config, new BitPdfReaderConfig { Id = PdfId }); + parameters.Add(p => p.Horizontal, true); + parameters.Add(p => p.IsEnabled, false); + }); + + var root = component.Find(".bit-pdr"); + + Assert.IsTrue(root.ClassList.Contains("bit-pdr-hor")); + Assert.IsTrue(root.ClassList.Contains("bit-dis")); + } + + [TestMethod] + public void BitPdfReaderShouldApplyCanvasClassAndStyle() + { + SetupJSInterop(); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Config, new BitPdfReaderConfig { Id = PdfId }); + parameters.Add(p => p.CanvasClass, "canvas-class"); + parameters.Add(p => p.CanvasStyle, "height:100px;"); + }); + + var canvas = component.Find("canvas"); + + Assert.IsTrue(canvas.ClassList.Contains("canvas-class")); + Assert.IsTrue((canvas.GetAttribute("style") ?? string.Empty).Contains("height:100px")); + } +} From 4874691de6dcc8457ab96197baf1bcd968b0113a Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Wed, 26 Nov 2025 15:55:42 +0330 Subject: [PATCH 23/39] feat(deps): update project dependencies #11757 (#11758) --- .../Bit.BlazorUI.Demo.Server.csproj | 2 +- .../Bit.BlazorUI.Demo.Client.Maui.csproj | 4 +-- .../Bit.BlazorUI.Demo.Client.Windows.csproj | 2 +- .../Bit.Butil.Demo.Maui.csproj | 4 +-- .../src/Directory.Packages.props | 29 +++++++++---------- .../Boilerplate.Server.Api.csproj | 1 - .../Boilerplate.Server.Web.csproj | 1 - .../Bit.Websites.Platform.Server.csproj | 2 +- 8 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj index 69f3cf4fc8..2f6e995406 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj @@ -32,7 +32,7 @@ - +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj index cc79c052e8..fb3a60bb5c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj @@ -117,8 +117,8 @@ - - + + - + - + - + - + - + @@ -76,9 +76,9 @@ - - - + + + @@ -114,9 +114,8 @@ - - + diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj index 23f0b10ed4..5c34249e00 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj @@ -39,7 +39,6 @@ - diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj index 77ca303941..c2d720bc6b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj @@ -17,7 +17,6 @@ -
diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj index 56df9d3cea..ba9cca1914 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj @@ -27,7 +27,7 @@ - + From 21a7ab38feff5e0561c41234dc20b11c10b9a637 Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Wed, 26 Nov 2025 16:38:38 +0330 Subject: [PATCH 24/39] feat(templates): use auto-clone feature of the fusion cache to prevent further scale out issues when swtiching to redis #11759 (#11760) --- .../Bit.Boilerplate/.github/copilot-instructions.md | 1 + .../Bit.Boilerplate/src/Directory.Packages.props | 1 + .../Boilerplate.Server.Shared.csproj | 3 ++- .../Extensions/WebApplicationBuilderExtensions.cs | 7 ++++++- .../src/Shared/Exceptions/KnownException.cs | 4 ++-- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md b/src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md index 352c0b5382..1ec275ff24 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md @@ -60,6 +60,7 @@ The solution is organized into the following projects. Understand their roles to - **DeepWiki**: Provides access to an extensive knowledge base for the `bitfoundation/bitplatform` and `riok/mapperly` repositories. - **Website Fetcher**: Gathers information from URLs provided by the user, using `fetch` or `get_web_pages` tools. +- **Microsoft Learn**: Provides access to official Microsoft documentation and code samples for Azure, .NET Aspire, .NET MAUI, Entity Framework Core, SignalR, Microsoft.Extensions.AI, SQL Server, and other Microsoft technologies. Use `microsoft_docs_search` to find relevant documentation and `microsoft_code_sample_search` to find official code examples. ## 5. Mandatory Workflow diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props index 9fc9ada0e1..22428bff1c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props @@ -134,5 +134,6 @@ +
\ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Boilerplate.Server.Shared.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Boilerplate.Server.Shared.csproj index f8dd419514..48f3946f50 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Boilerplate.Server.Shared.csproj +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Boilerplate.Server.Shared.csproj @@ -29,8 +29,9 @@ - + + diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Extensions/WebApplicationBuilderExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Extensions/WebApplicationBuilderExtensions.cs index bb93e671c1..6060bc2ddc 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Extensions/WebApplicationBuilderExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Extensions/WebApplicationBuilderExtensions.cs @@ -14,6 +14,8 @@ using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; +using ZiggyCreatures.Caching.Fusion; +using ZiggyCreatures.Caching.Fusion.Serialization.SystemTextJson; namespace Microsoft.Extensions.Hosting; @@ -44,7 +46,10 @@ public static TBuilder AddServerSharedServices(this TBuilder builder) }, excludeDefaultPolicy: true); }); - services.AddFusionCache(); + services.AddFusionCache() + // Auto-clone cached objects to avoid further issues after scaling out and switching to distributed caching. + .WithOptions(opt => opt.DefaultEntryOptions.EnableAutoClone = true) + .WithSerializer(new FusionCacheSystemTextJsonSerializer()); services.AddFusionOutputCache(); // For ASP.NET Core Output Caching with FusionCache services.AddHttpContextAccessor(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Exceptions/KnownException.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Exceptions/KnownException.cs index e052ebba36..7f98b24dfc 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Exceptions/KnownException.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Exceptions/KnownException.cs @@ -1,6 +1,6 @@ -namespace Boilerplate.Shared.Exceptions; +namespace Boilerplate.Shared.Exceptions; -public abstract partial class KnownException : Exception +public abstract partial class KnownException : ApplicationException { public KnownException(string message) : base(message) From 5989e7c1326ca7392155f84b6f0227e487f24b0e Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Thu, 27 Nov 2025 09:20:05 +0330 Subject: [PATCH 25/39] feat(blazorui): add missing tests to BitProPanel test file #11761 (#11762) --- .../Extras/ProPanel/BitProPanelTests.cs | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ProPanel/BitProPanelTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ProPanel/BitProPanelTests.cs index be0b2ba6e8..7b4e986e8b 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ProPanel/BitProPanelTests.cs +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/ProPanel/BitProPanelTests.cs @@ -1,4 +1,6 @@ using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Bit.BlazorUI.Tests.Components.Extras.ProPanel; @@ -51,4 +53,115 @@ public void BitProPanelHeaderContentTest() elementContent.MarkupMatches(headerContent); } + + [TestMethod] + public void BitProPanelShouldRenderHeaderTextAndCloseButton() + { + var com = RenderComponent(parameters => + { + parameters.Add(p => p.IsOpen, true); + parameters.Add(p => p.HeaderText, "Header Text"); + parameters.Add(p => p.ShowCloseButton, true); + }); + + var header = com.Find(".bit-ppl-hdr"); + var closeButton = com.Find(".bit-ppl-cls"); + + Assert.IsNotNull(closeButton); + Assert.AreEqual("Header Text", header.TextContent); + } + + [TestMethod] + public void BitProPanelShouldRenderFooterTextWhenFooterTemplateMissing() + { + var com = RenderComponent(parameters => + { + parameters.Add(p => p.IsOpen, true); + parameters.Add(p => p.FooterText, "Footer Text"); + }); + + var footer = com.Find(".bit-ppl-fcn"); + + Assert.AreEqual("Footer Text", footer.TextContent); + } + + [TestMethod] + public void BitProPanelModeFullShouldApplyCssClass() + { + var com = RenderComponent(parameters => + { + parameters.Add(p => p.IsOpen, true); + parameters.Add(p => p.ModeFull, true); + }); + + var root = com.Find(".bit-ppl"); + + Assert.IsTrue(root.ClassList.Contains("bit-ppl-mfl")); + } + + [TestMethod] + public void BitProPanelShouldInvokeOnOpenWhenOpening() + { + var opened = 0; + var isOpen = false; + + var com = RenderComponent(parameters => + { + parameters.Bind(p => p.IsOpen, isOpen, v => isOpen = v); + parameters.Add(p => p.OnOpen, EventCallback.Factory.Create(this, () => opened++)); + }); + + com.SetParametersAndRender(p => p.Add(x => x.IsOpen, true)); + + com.WaitForAssertion(() => + { + Assert.AreEqual(1, opened); + }); + } + + [TestMethod] + public void BitProPanelShouldInvokeOnDismissWhenCloseButtonClicked() + { + var dismissed = 0; + var isOpen = true; + + var com = RenderComponent(parameters => + { + parameters.Bind(p => p.IsOpen, isOpen, v => isOpen = v); + parameters.Add(p => p.OnDismiss, EventCallback.Factory.Create(this, () => dismissed++)); + parameters.Add(p => p.ShowCloseButton, true); + }); + + com.Find(".bit-ppl-cls").Click(); + + com.WaitForAssertion(() => + { + Assert.AreEqual(1, dismissed); + Assert.IsFalse(isOpen); + }); + } + + [TestMethod] + public void BitProPanelShouldRespectIsEnabled() + { + var dismissed = 0; + var isOpen = true; + + var com = RenderComponent(parameters => + { + parameters.Bind(p => p.IsOpen, isOpen, v => isOpen = v); + parameters.Add(p => p.OnDismiss, EventCallback.Factory.Create(this, () => dismissed++)); + parameters.Add(p => p.ShowCloseButton, true); + parameters.Add(p => p.IsEnabled, false); + }); + + com.Find(".bit-ppl-cls").Click(); + + com.WaitForAssertion(() => + { + Assert.AreEqual(0, dismissed); + Assert.IsTrue(isOpen); + Assert.IsTrue(com.Find(".bit-ppl").ClassList.Contains("bit-dis")); + }); + } } From d851f67f560014ead2fca3c71f1b60ab36420b02 Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Sat, 29 Nov 2025 03:40:03 +0330 Subject: [PATCH 26/39] feat(templates): applied changes in bit Boilerplate for further offline db sync feature #11765 (#11766) --- .../.docs/01- Entity Framework Core.md | 24 +-- .../.docs/02- DTOs, Mappers, and Mapperly.md | 12 +- .../.docs/03- API Controllers and OData.md | 52 ++--- ...Localization and Multi-language Support.md | 6 +- ...Pages, Components, Styling & Navigation.md | 4 +- ...ndency Injection & Service Registration.md | 37 ++-- .../.docs/14- Response Caching System.md | 4 +- .../.github/copilot-instructions.md | 6 +- .../.github/prompts/getting-started.prompt.md | 6 +- .../Bit.Boilerplate/.vscode/mcp.json | 4 + .../Pages/Categories/CategoriesPage.razor.cs | 2 +- .../Pages/Management/RolesPage.razor.cs | 2 +- .../Pages/OfflineDatabaseDemoPage.razor.cs | 2 +- .../Pages/Products/ProductsPage.razor.cs | 2 +- .../Components/Pages/TodoPage.razor | 2 +- .../Components/Pages/TodoPage.razor.cs | 4 +- ...ineDbContext.cs => AppOfflineDbContext.cs} | 4 +- ... AppOfflineDbContextAssemblyAttributes.cs} | 2 +- .../CompiledModel/AppOfflineDbContextModel.cs | 46 ++++ .../AppOfflineDbContextModelBuilder.cs | 26 +++ .../CompiledModel/OfflineDbContextModel.cs | 47 ---- .../OfflineDbContextModelBuilder.cs | 27 --- .../Data/CompiledModel/UserDtoEntityType.cs | 203 +++++++++--------- ....cs => 20251128225322_Initial.Designer.cs} | 14 +- ...Migration.cs => 20251128225322_Initial.cs} | 11 +- ...cs => AppOfflineDbContextModelSnapshot.cs} | 12 +- .../Boilerplate.Client.Core/Data/README.md | 14 +- .../IClientCoreServiceCollectionExtensions.cs | 6 +- .../Bit.Boilerplate/src/Directory.Build.props | 2 +- .../Categories/CategoryController.cs | 10 +- .../Identity/RoleManagementController.cs | 11 +- .../Controllers/Products/ProductController.cs | 14 +- .../Controllers/Todo/TodoItemController.cs | 6 +- .../Data/AppDbContext.cs | 10 +- .../Category/CategoryConfiguration.cs | 12 +- .../Chatbot/SystemPromptConfiguration.cs | 4 +- .../Product/ProductConfiguration.cs | 102 ++++----- .../Mappers/IdentityMapper.cs | 27 ++- .../Models/Categories/Category.cs | 4 +- .../Models/Chatbot/SystemPrompt.cs | 2 +- .../Models/Products/Product.cs | 2 +- .../Models/Todo/TodoItem.cs | 4 +- .../Categories/ICategoryController.cs | 8 +- .../Identity/IRoleManagementController.cs | 4 +- .../Products/IProductController.cs | 8 +- .../src/Shared/Dtos/AppJsonContext.cs | 6 +- .../src/Shared/Dtos/Categories/CategoryDto.cs | 4 +- .../Shared/Dtos/Chatbot/SystemPromptDto.cs | 2 +- .../src/Shared/Dtos/Identity/RoleDto.cs | 2 - .../src/Shared/Dtos/Identity/UserDto.cs | 4 +- .../{PagedResultDto.cs => PagedResponse.cs} | 6 +- .../src/Shared/Dtos/Products/ProductDto.cs | 4 +- .../src/Shared/Dtos/Todo/TodoItemDto.cs | 4 +- 53 files changed, 418 insertions(+), 425 deletions(-) rename src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/{OfflineDbContext.cs => AppOfflineDbContext.cs} (89%) rename src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/{OfflineDbContextAssemblyAttributes.cs => AppOfflineDbContextAssemblyAttributes.cs} (65%) create mode 100644 src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs create mode 100644 src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModelBuilder.cs delete mode 100644 src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextModel.cs delete mode 100644 src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextModelBuilder.cs rename src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/{20250416163233_InitialMigration.Designer.cs => 20251128225322_Initial.Designer.cs} (90%) rename src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/{20250416163233_InitialMigration.cs => 20251128225322_Initial.cs} (74%) rename src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/{OfflineDbContextModelSnapshot.cs => AppOfflineDbContextModelSnapshot.cs} (91%) rename src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/{PagedResultDto.cs => PagedResponse.cs} (58%) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/01- Entity Framework Core.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/01- Entity Framework Core.md index ae7585fdc3..30cfc0aed3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/01- Entity Framework Core.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/01- Entity Framework Core.md @@ -94,15 +94,15 @@ public partial class Category public string? Color { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; public IList Products { get; set; } = []; } ``` -### **ConcurrencyStamp** +### **Version** Concurrency Stamp ```csharp -public byte[] ConcurrencyStamp { get; set; } = []; +public byte[] Version { get; set; } = []; ``` - **Critical for optimistic concurrency control** - Configured as a **row version** in SQL Server @@ -220,19 +220,19 @@ public partial class CategoryConfiguration : IEntityTypeConfiguration builder.HasIndex(p => p.Name).IsUnique(); // Seed initial data - var defaultConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + var defaultVersion = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; builder.HasData( new Category { Id = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), Name = "Ford", Color = "#FFCD56", - ConcurrencyStamp = defaultConcurrencyStamp + Version = defaultVersion }, new Category { Id = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), Name = "Nissan", Color = "#FF6384", - ConcurrencyStamp = defaultConcurrencyStamp + Version = defaultVersion } ); } @@ -382,12 +382,12 @@ For client-side databases: ### Location and Technology -**DbContext Location:** [`/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs`](/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs) +**DbContext Location:** [`/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs`](/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs) **Technology:** [bit Besql](https://bitplatform.dev/besql) - EF Core SQLite for Blazor ```csharp -public partial class OfflineDbContext(DbContextOptions options) : DbContext(options) +public partial class AppOfflineDbContext(DbContextOptions options) : DbContext(options) { public virtual DbSet Users { get; set; } @@ -425,7 +425,7 @@ This ensures: ### Creating Client-Side Migrations -To add a migration for `OfflineDbContext`, follow these steps: +To add a migration for `AppOfflineDbContext`, follow these steps: #### Option 1: Using Package Manager Console (Visual Studio) @@ -433,20 +433,20 @@ To add a migration for `OfflineDbContext`, follow these steps: 2. Set `Boilerplate.Client.Core` as the **Default Project** in Package Manager Console 3. Run: ```powershell -Add-Migration YourMigrationName -OutputDir Data\Migrations -Context OfflineDbContext -Verbose +Add-Migration YourMigrationName -OutputDir Data\Migrations -Context AppOfflineDbContext -Verbose ``` #### Option 2: Using dotnet CLI Open a terminal in the `Boilerplate.Server.Web` project directory and run: ```bash -dotnet ef migrations add YourMigrationName --context OfflineDbContext --output-dir Data/Migrations --project ../Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj --verbose +dotnet ef migrations add YourMigrationName --context AppOfflineDbContext --output-dir Data/Migrations --project ../Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj --verbose ``` **Important Notes:** - Ensure the solution builds successfully before running migration commands - Do **NOT** run `Update-Database` for client-side migrations -- The migration is automatically applied via `MigrateAsync()` when the app starts using OfflineDbContext +- The migration is automatically applied via `MigrateAsync()` when the app starts using AppOfflineDbContext ### Additional Resources diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/02- DTOs, Mappers, and Mapperly.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/02- DTOs, Mappers, and Mapperly.md index 6d02605d59..064363fcd1 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/02- DTOs, Mappers, and Mapperly.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/02- DTOs, Mappers, and Mapperly.md @@ -40,7 +40,7 @@ public partial class CategoryDto public int ProductsCount { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; } ``` @@ -55,7 +55,7 @@ public partial class CategoryDto 3. **Calculated Properties**: `ProductsCount` is a computed property that shows the count of products in a category (not stored in the database) -4. **ConcurrencyStamp**: Used for optimistic concurrency control to prevent conflicting updates +4. **Version**: Used for optimistic concurrency control to prevent conflicting updates ### Example: UserDto @@ -117,7 +117,7 @@ public partial class AppJsonContext : JsonSerializerContext **You must add a `[JsonSerializable]` attribute whenever you:** - Create a new DTO -- Return a `List` or `PagedResult` of a DTO from an API +- Return a `List` or `PagedResponse` of a DTO from an API - Use a DTO in SignalR communication --- @@ -166,7 +166,7 @@ public partial class Category public string? Color { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; public IList Products { get; set; } = []; } @@ -220,7 +220,7 @@ SELECT c.Name, c.Color, (SELECT COUNT(*) FROM Products WHERE CategoryId = c.Id) AS ProductsCount, - c.ConcurrencyStamp + c.Version FROM Categories c ``` @@ -261,7 +261,7 @@ return DbContext.Categories.Select(c => new CategoryDto Name = c.Name, Color = c.Color, ProductsCount = c.Products.Count, - ConcurrencyStamp = c.ConcurrencyStamp + Version = c.Version }); ``` diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/03- API Controllers and OData.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/03- API Controllers and OData.md index 3a1aff842d..6bda2c6478 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/03- API Controllers and OData.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/03- API Controllers and OData.md @@ -12,7 +12,7 @@ This stage provides a comprehensive exploration of the backend API architecture, 2. [Dependency Injection with [AutoInject]](#dependency-injection-with-autoinject) 3. [Reading Data with IQueryable](#reading-data-with-iqueryable) 4. [OData Query Options Support](#odata-query-options-support) -5. [PagedResult for Total Count](#pagedresult-for-total-count) +5. [PagedResponse for Total Count](#PagedResponse-for-total-count) 6. [Data Security and Permissions](#data-security-and-permissions) 7. [Live Demos](#live-demos) 8. [Performance Optimization](#performance-optimization) @@ -280,26 +280,26 @@ private void PrepareGridDataProvider() This code: 1. Creates an `ODataQuery` object with pagination, sorting, and filtering 2. Applies it to the API request using `WithQuery()` -3. Gets a `PagedResult` containing both the data and total count +3. Gets a `PagedResponse` containing both the data and total count --- -## PagedResult for Total Count +## PagedResponse for Total Count -When a client (like a data grid) needs to know both the **page data** AND the **total count** of records, use `PagedResult`. +When a client (like a data grid) needs to know both the **page data** AND the **total count** of records, use `PagedResponse`. -### The PagedResult Class +### The PagedResponse Class -**File**: [`src/Shared/Dtos/PagedResultDto.cs`](/src/Shared/Dtos/PagedResultDto.cs) +**File**: [`src/Shared/Dtos/PagedResponseDto.cs`](/src/Shared/Dtos/PagedResponseDto.cs) ```csharp -public partial class PagedResult +public partial class PagedResponse { public T[] Items { get; set; } = []; public long TotalCount { get; set; } [JsonConstructor] - public PagedResult(T[] items, long totalCount) + public PagedResponse(T[] items, long totalCount) { Items = items; TotalCount = totalCount; @@ -307,7 +307,7 @@ public partial class PagedResult } ``` -### Why Use PagedResult? +### Why Use PagedResponse? Without total count, the client doesn't know: - How many pages exist @@ -320,7 +320,7 @@ Without total count, the client doesn't know: ```csharp [HttpGet] -public async Task> GetCategories( +public async Task> GetCategories( ODataQueryOptions odataQuery, CancellationToken cancellationToken) { @@ -337,7 +337,7 @@ public async Task> GetCategories( .TakeIf(odataQuery.Top is not null, odataQuery.Top?.Value); // Return both data and count - return new PagedResult( + return new PagedResponse( await query.ToArrayAsync(cancellationToken), totalCount); } @@ -347,7 +347,7 @@ public async Task> GetCategories( 1. Apply filters/sorting but ignore `$top`/`$skip` 2. Count the total filtered results 3. Apply pagination -4. Return data + count in `PagedResult` +4. Return data + count in `PagedResponse` --- @@ -522,10 +522,10 @@ public IQueryable Get() } ``` -#### PagedResult with Total Count +#### PagedResponse with Total Count ```csharp [HttpGet] -public async Task> GetCategories( +public async Task> GetCategories( ODataQueryOptions odataQuery, CancellationToken cancellationToken) { @@ -538,7 +538,7 @@ public async Task> GetCategories( query = query.SkipIf(odataQuery.Skip is not null, odataQuery.Skip?.Value) .TakeIf(odataQuery.Top is not null, odataQuery.Top?.Value); - return new PagedResult( + return new PagedResponse( await query.ToArrayAsync(cancellationToken), totalCount); } @@ -601,8 +601,8 @@ public async Task Update(CategoryDto dto, CancellationToken cancell #### Delete with Business Logic Validation ```csharp -[HttpDelete("{id}/{concurrencyStamp}")] -public async Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken) +[HttpDelete("{id}/{version}")] +public async Task Delete(Guid id, string version, CancellationToken cancellationToken) { // Business rule: Cannot delete category if it has products if (await DbContext.Products.AnyAsync(p => p.CategoryId == id, cancellationToken)) @@ -613,7 +613,7 @@ public async Task Delete(Guid id, string concurrencyStamp, CancellationToken can DbContext.Categories.Remove(new() { Id = id, - ConcurrencyStamp = Convert.FromHexString(concurrencyStamp) + Version = Convert.FromHexString(version) }); await DbContext.SaveChangesAsync(cancellationToken); @@ -646,7 +646,7 @@ This controller shows more advanced features: #### Semantic Search Integration ```csharp [HttpGet("{searchQuery}")] -public async Task> SearchProducts( +public async Task> SearchProducts( string searchQuery, ODataQueryOptions odataQuery, CancellationToken cancellationToken) @@ -663,7 +663,7 @@ public async Task> SearchProducts( query = query.SkipIf(odataQuery.Skip is not null, odataQuery.Skip?.Value) .TakeIf(odataQuery.Top is not null, odataQuery.Top?.Value); - return new PagedResult( + return new PagedResponse( await query.ToArrayAsync(cancellationToken), totalCount); } @@ -710,7 +710,7 @@ public interface ICategoryController : IAppController Task Get(Guid id, CancellationToken cancellationToken); [HttpGet] - Task> GetCategories(CancellationToken cancellationToken) => default!; + Task> GetCategories(CancellationToken cancellationToken) => default!; [HttpGet] Task> Get(CancellationToken cancellationToken) => default!; @@ -721,8 +721,8 @@ public interface ICategoryController : IAppController [HttpPut] Task Update(CategoryDto dto, CancellationToken cancellationToken); - [HttpDelete("{id}/{concurrencyStamp}")] - Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken); + [HttpDelete("{id}/{version}")] + Task Delete(Guid id, string version, CancellationToken cancellationToken); } ``` @@ -741,7 +741,7 @@ public partial class CategoryController : AppControllerBase, ICategoryController } [HttpGet] - public async Task> GetCategories( + public async Task> GetCategories( ODataQueryOptions odataQuery, // Server-specific parameter CancellationToken cancellationToken) { @@ -773,7 +773,7 @@ public partial class ProductsPage }; // Apply query and call API - var pagedResult = await productController + var PagedResponse = await productController .WithQuery(query.ToString()) .GetProducts(CurrentCancellationToken); } @@ -800,7 +800,7 @@ public interface ICategoryController : IAppController { // Server accepts ODataQueryOptions, but client doesn't pass it [HttpGet] - Task> GetCategories(CancellationToken cancellationToken) => default!; + Task> GetCategories(CancellationToken cancellationToken) => default!; } ``` diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/05- Localization and Multi-language Support.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/05- Localization and Multi-language Support.md index a952c61ed3..e88fdc1e56 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/05- Localization and Multi-language Support.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/05- Localization and Multi-language Support.md @@ -137,7 +137,7 @@ public partial class CategoryDto public int ProductsCount { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; } ``` @@ -382,8 +382,8 @@ public async Task Get(Guid id, CancellationToken cancellationToken) return dto; } -[HttpDelete("{id}/{concurrencyStamp}")] -public async Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken) +[HttpDelete("{id}/{version}")] +public async Task Delete(Guid id, string version, CancellationToken cancellationToken) { if (await DbContext.Products.AnyAsync(p => p.CategoryId == id, cancellationToken)) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/08- Blazor Pages, Components, Styling & Navigation.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/08- Blazor Pages, Components, Styling & Navigation.md index ab49647526..79437d3303 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/08- Blazor Pages, Components, Styling & Navigation.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/08- Blazor Pages, Components, Styling & Navigation.md @@ -113,7 +113,7 @@ public partial class ProductsPage if (deletingProduct is null) return; await productController.Delete(deletingProduct.Id, - deletingProduct.ConcurrencyStamp.ToStampString(), + deletingProduct.Version.ToStampString(), CurrentCancellationToken); await RefreshData(); @@ -701,7 +701,7 @@ public partial class ProductsPage private async Task DeleteProduct() { await productController.Delete(deletingProduct.Id, - deletingProduct.ConcurrencyStamp.ToStampString(), + deletingProduct.Version.ToStampString(), CurrentCancellationToken); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/09- Dependency Injection & Service Registration.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/09- Dependency Injection & Service Registration.md index 3e67441b40..1e6b43b5c8 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/09- Dependency Injection & Service Registration.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/09- Dependency Injection & Service Registration.md @@ -188,31 +188,26 @@ This allows each platform to provide its own native implementation while sharing ## Example: Adding a New Service -Let's say you want to add a `ProductSyncService` that works on all client platforms: +Let's say you want to add a `FeedbackService` that works on all client platforms: ### Step 1: Create the Service ```csharp -// src/Client/Boilerplate.Client.Core/Services/ProductSyncService.cs +// src/Client/Boilerplate.Client.Core/Services/FeedbackService.cs namespace Boilerplate.Client.Core.Services; -public partial class ProductSyncService +public partial class FeedbackService { - [AutoInject] private IProductController productController = default!; - [AutoInject] private OfflineDbContext offlineDb = default!; - [AutoInject] private ILogger logger = default!; + [AutoInject] private IFeedbackController feedbackController = default!; + [AutoInject] private ILogger logger = default!; - public async Task SyncProductsAsync() + public async Task SendFeedbackAsync(string message, CancellationToken cancellationToken = default) { - logger.LogInformation("Starting product sync..."); + logger.LogInformation("Sending user feedback..."); - var products = await productController.GetProducts(); + await feedbackController.SendFeedback(new FeedbackDto { Message = message }, cancellationToken); - // Save to offline database - await offlineDb.Products.AddRangeAsync(products); - await offlineDb.SaveChangesAsync(); - - logger.LogInformation("Product sync completed. Synced {Count} products.", products.Count); + logger.LogInformation("Feedback sent successfully."); } } ``` @@ -225,8 +220,8 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle { // ... existing services ... - // Register as Sessioned because sync state should be per-user - services.AddSessioned(); + // Register as Sessioned because feedback state should be per-user + services.AddSessioned(); return services; } @@ -236,16 +231,16 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle ```csharp // In any component or page -public partial class ProductPage : AppPageBase +public partial class FeedbackPage : AppPageBase { - [AutoInject] private ProductSyncService productSyncService = default!; + [AutoInject] private FeedbackService feedbackService = default!; - protected override async Task OnInitAsync() + private async Task OnSubmitFeedback(string message) { try { - await productSyncService.SyncProductsAsync(); - SnackBarService.Success(Localizer[nameof(AppStrings.ProductsSyncedSuccessfully)]); + await feedbackService.SendFeedbackAsync(message); + SnackBarService.Success(Localizer[nameof(AppStrings.FeedbackSentSuccessfully)]); } catch (Exception ex) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/14- Response Caching System.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/14- Response Caching System.md index 95bc69156d..077b553185 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/14- Response Caching System.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/14- Response Caching System.md @@ -247,8 +247,8 @@ public async Task Update(ProductDto dto, CancellationToken cancellat return entityToUpdate.Map(); } -[HttpDelete("{id}/{concurrencyStamp}")] -public async Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken) +[HttpDelete("{id}/{version}")] +public async Task Delete(Guid id, string version, CancellationToken cancellationToken) { // ... delete logic ... await DbContext.SaveChangesAsync(cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md b/src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md index 1ec275ff24..16b4dbbdc2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md @@ -130,13 +130,13 @@ Example 1: `OnClick="WrapHandled(MyMethod)"` instead of `OnClick="MyMethod"`. Example 2: `OnClick="WrapHandled(async () => await MyMethod())"` instead of `OnClick="async () => await MyMethod()"`. 16. **Use OData Query Options**: Leverage `[EnableQuery]` and `ODataQueryOptions` for efficient data filtering and pagination. 17. **Follow Mapperly Conventions**: Use **partial static classes and extensions methods** with Mapperly for high-performance object mapping. -18. **Handle Concurrency**: Always use `ConcurrencyStamp` for optimistic concurrency control in update and delete operations. +18. **Handle Concurrency**: Always use `byte[] Version` for optimistic concurrency control in update and delete operations. ## Instructions for adding new model/entity to ef-core DbContext / Database Create the Entity Model - **Location**: `Boilerplate.Server.Api's Models folder` - **Requirements**: - - Include `Id`, `ConcurrencyStamp` properties + - Include `Id`, `Version` properties - Add appropriate navigation properties - Use nullable reference types - Add data annotations as needed @@ -155,7 +155,7 @@ Create the DTO - Use `[DtoResourceType(typeof(AppStrings))]` attribute - Add validation attributes: `[Required]`, `[MaxLength]`, `[Display]` - Use `nameof(AppStrings.PropertyName)` for error messages and display names - - Include `Id`, `ConcurrencyStamp` properties + - Include `Id`, `Version` properties - Add calculated properties if needed (e.g., `ProductsCount`) - Add `[JsonSerializable(typeof({DtoName}))]` to AppJsonContext.cs diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.github/prompts/getting-started.prompt.md b/src/Templates/Boilerplate/Bit.Boilerplate/.github/prompts/getting-started.prompt.md index 4df3c066ac..fe93889c82 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.github/prompts/getting-started.prompt.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.github/prompts/getting-started.prompt.md @@ -187,11 +187,11 @@ In this stage, you will explain the following topics: - **OData Query Options Support**: Explain that the project supports most OData query options: - ✅ **Supported**: `$filter`, `$top`, `$skip`, `$orderby`, `$select` - ❌ **Not Supported yet**: `$count` -- **PagedResult for Total Count**: When the client (e.g., a data grid) needs to know both the page data AND the total count of records, use `PagedResult` instead of returning `IQueryable`: +- **PagedResponse for Total Count**: When the client (e.g., a data grid) needs to know both the page data AND the total count of records, use `PagedResponse` instead of returning `IQueryable`: - **Example**: The `GetCategories` method in `CategoryController.cs`: ```csharp [HttpGet] - public async Task> GetCategories(ODataQueryOptions odataQuery, CancellationToken cancellationToken) + public async Task> GetCategories(ODataQueryOptions odataQuery, CancellationToken cancellationToken) { var query = (IQueryable)odataQuery.ApplyTo(Get(), ignoreQueryOptions: AllowedQueryOptions.Top | AllowedQueryOptions.Skip); @@ -200,7 +200,7 @@ In this stage, you will explain the following topics: query = query.SkipIf(odataQuery.Skip is not null, odataQuery.Skip?.Value) .TakeIf(odataQuery.Top is not null, odataQuery.Top?.Value); - return new PagedResult(await query.ToArrayAsync(cancellationToken), totalCount); + return new PagedResponse(await query.ToArrayAsync(cancellationToken), totalCount); } ``` - This allows the client to display pagination info like "Showing 10 of 250 items" diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.vscode/mcp.json b/src/Templates/Boilerplate/Bit.Boilerplate/.vscode/mcp.json index 8e9f069007..2ebbd4540a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.vscode/mcp.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.vscode/mcp.json @@ -3,6 +3,10 @@ "DeepWiki": { "type": "sse", "url": "https://mcp.deepwiki.com/mcp" + }, + "MicrosoftLearn": { + "type": "http", + "url": "https://learn.microsoft.com/api/mcp" } } } \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.cs index 0f71b9f85d..1f25d2df38 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Categories/CategoriesPage.razor.cs @@ -99,7 +99,7 @@ private async Task DeleteCategory() try { - await categoryController.Delete(deletingCategory.Id, deletingCategory.ConcurrencyStamp.ToStampString(), CurrentCancellationToken); + await categoryController.Delete(deletingCategory.Id, deletingCategory.Version.ToStampString(), CurrentCancellationToken); await RefreshData(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Management/RolesPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Management/RolesPage.razor.cs index 824dd81c10..95d48702c1 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Management/RolesPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Management/RolesPage.razor.cs @@ -170,7 +170,7 @@ private async Task DeleteRole() if (await AuthManager.TryEnterElevatedAccessMode(CurrentCancellationToken) is false) return; var roleId = Guid.Parse(selectedRoleItem.Key!); var role = ((RoleDto)selectedRoleItem.Data!); - await roleManagementController.Delete(roleId, role.ConcurrencyStamp!, CurrentCancellationToken); + await roleManagementController.Delete(roleId, CurrentCancellationToken); await LoadAllRoles(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor.cs index 5600f55632..c37f665cc3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor.cs @@ -6,7 +6,7 @@ namespace Boilerplate.Client.Core.Components.Pages; public partial class OfflineDatabaseDemoPage { - [AutoInject] IDbContextFactory dbContextFactory = default!; + [AutoInject] IDbContextFactory dbContextFactory = default!; private bool isSaving; private bool isLoading = true; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.cs index 1e1fa59d2f..f5dec5f887 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Products/ProductsPage.razor.cs @@ -113,7 +113,7 @@ private async Task DeleteProduct() try { - await productController.Delete(deletingProduct.Id, deletingProduct.ConcurrencyStamp.ToStampString(), CurrentCancellationToken); + await productController.Delete(deletingProduct.Id, deletingProduct.Version.ToStampString(), CurrentCancellationToken); await RefreshData(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor index 5695eb5ed7..ceb3f3cfcb 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor @@ -87,7 +87,7 @@ Styles="@(new() { Label = todo.IsDone ? "text-decoration:line-through" : "" })" DefaultValue="todo.IsDone" OnChange="() => ToggleIsDone(todo)" /> - @todo.Date.ToLocalTime().ToString("F") + @todo.UpdatedAt.ToLocalTime().ToString("F") diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor.cs index 85657dfd0f..4943e9244c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor.cs @@ -67,12 +67,12 @@ private void FilterViewTodoItems() if (isDescendingSort) { items = items.OrderByDescendingIf(selectedSort == nameof(AppStrings.Alphabetical), t => t.Title!) - .OrderByDescendingIf(selectedSort == nameof(AppStrings.Date), t => t.Date!); + .OrderByDescendingIf(selectedSort == nameof(AppStrings.Date), t => t.UpdatedAt!); } else { items = items.OrderByIf(selectedSort == nameof(AppStrings.Alphabetical), t => t.Title!) - .OrderByIf(selectedSort == nameof(AppStrings.Date), t => t.Date!); + .OrderByIf(selectedSort == nameof(AppStrings.Date), t => t.UpdatedAt!); } viewTodoItems = items.ToList(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs similarity index 89% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs index 67e3f7a958..727925be6a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs @@ -1,10 +1,10 @@ -using Boilerplate.Shared.Dtos.Identity; +using Boilerplate.Shared.Dtos.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Boilerplate.Client.Core.Data; -public partial class OfflineDbContext(DbContextOptions options) : DbContext(options) +public partial class AppOfflineDbContext(DbContextOptions options) : DbContext(options) { public virtual DbSet Users { get; set; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextAssemblyAttributes.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextAssemblyAttributes.cs similarity index 65% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextAssemblyAttributes.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextAssemblyAttributes.cs index 491acb9707..bf4f2ab1fb 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextAssemblyAttributes.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextAssemblyAttributes.cs @@ -5,4 +5,4 @@ #pragma warning disable 219, 612, 618 #nullable disable -[assembly: DbContextModel(typeof(OfflineDbContext), typeof(OfflineDbContextModel))] +[assembly: DbContextModel(typeof(AppOfflineDbContext), typeof(AppOfflineDbContextModel))] diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs new file mode 100644 index 0000000000..e3bcf531da --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs @@ -0,0 +1,46 @@ +// +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace Boilerplate.Client.Core.Data; + +[DbContext(typeof(AppOfflineDbContext))] +public partial class AppOfflineDbContextModel : RuntimeModel +{ + private static readonly bool _useOldBehavior31751 = + System.AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31751", out var enabled31751) && enabled31751; + + static AppOfflineDbContextModel() + { + var model = new AppOfflineDbContextModel(); + + if (_useOldBehavior31751) + { + model.Initialize(); + } + else + { + var thread = new System.Threading.Thread(RunInitialization, 10 * 1024 * 1024); + thread.Start(); + thread.Join(); + + void RunInitialization() + { + model.Initialize(); + } + } + + model.Customize(); + _instance = (AppOfflineDbContextModel)model.FinalizeModel(); + } + + private static AppOfflineDbContextModel _instance; + public static IModel Instance => _instance; + + partial void Initialize(); + + partial void Customize(); +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModelBuilder.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModelBuilder.cs new file mode 100644 index 0000000000..fbfa6c3021 --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModelBuilder.cs @@ -0,0 +1,26 @@ +// +using System; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace Boilerplate.Client.Core.Data; + +public partial class AppOfflineDbContextModel +{ + private AppOfflineDbContextModel() + : base(skipDetectChanges: false, modelId: new Guid("0687ec37-33db-4238-976d-993653e85faf"), entityTypeCount: 1) + { + } + + partial void Initialize() + { + var userDto = UserDtoEntityType.Create(this); + + UserDtoEntityType.CreateAnnotations(userDto); + + AddAnnotation("ProductVersion", "10.0.0"); + } +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextModel.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextModel.cs deleted file mode 100644 index 5b072f4673..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextModel.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; - -#pragma warning disable 219, 612, 618 -#nullable disable - -namespace Boilerplate.Client.Core.Data -{ - [DbContext(typeof(OfflineDbContext))] - public partial class OfflineDbContextModel : RuntimeModel - { - private static readonly bool _useOldBehavior31751 = - System.AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31751", out var enabled31751) && enabled31751; - - static OfflineDbContextModel() - { - var model = new OfflineDbContextModel(); - - if (_useOldBehavior31751) - { - model.Initialize(); - } - else - { - var thread = new System.Threading.Thread(RunInitialization, 10 * 1024 * 1024); - thread.Start(); - thread.Join(); - - void RunInitialization() - { - model.Initialize(); - } - } - - model.Customize(); - _instance = (OfflineDbContextModel)model.FinalizeModel(); - } - - private static OfflineDbContextModel _instance; - public static IModel Instance => _instance; - - partial void Initialize(); - - partial void Customize(); - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextModelBuilder.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextModelBuilder.cs deleted file mode 100644 index 95363ed290..0000000000 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/OfflineDbContextModelBuilder.cs +++ /dev/null @@ -1,27 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; - -#pragma warning disable 219, 612, 618 -#nullable disable - -namespace Boilerplate.Client.Core.Data -{ - public partial class OfflineDbContextModel - { - private OfflineDbContextModel() - : base(skipDetectChanges: false, modelId: new Guid("de8f2fc8-013e-4376-8065-9a324ab466df"), entityTypeCount: 1) - { - } - - partial void Initialize() - { - var userDto = UserDtoEntityType.Create(this); - - UserDtoEntityType.CreateAnnotations(userDto); - - AddAnnotation("ProductVersion", "10.0.0-rc.1.25451.107"); - } - } -} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/UserDtoEntityType.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/UserDtoEntityType.cs index cf6e53c227..1d906aac0f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/UserDtoEntityType.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/UserDtoEntityType.cs @@ -10,109 +10,108 @@ #pragma warning disable 219, 612, 618 #nullable disable -namespace Boilerplate.Client.Core.Data +namespace Boilerplate.Client.Core.Data; + +[EntityFrameworkInternal] +public partial class UserDtoEntityType { - [EntityFrameworkInternal] - public partial class UserDtoEntityType + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) { - public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) - { - var runtimeEntityType = model.AddEntityType( - "Boilerplate.Shared.Dtos.Identity.UserDto", - typeof(UserDto), - baseEntityType, - propertyCount: 10, - keyCount: 1); - - var id = runtimeEntityType.AddProperty( - "Id", - typeof(Guid), - propertyInfo: typeof(UserDto).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - valueGenerated: ValueGenerated.OnAdd, - afterSaveBehavior: PropertySaveBehavior.Throw, - sentinel: new Guid("00000000-0000-0000-0000-000000000000")); - - var birthDate = runtimeEntityType.AddProperty( - "BirthDate", - typeof(DateTimeOffset?), - propertyInfo: typeof(UserDto).GetProperty("BirthDate", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - nullable: true, - valueConverter: new DateTimeOffsetToBinaryConverter()); - - var concurrencyStamp = runtimeEntityType.AddProperty( - "ConcurrencyStamp", - typeof(string), - propertyInfo: typeof(UserDto).GetProperty("ConcurrencyStamp", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - nullable: true); - - var email = runtimeEntityType.AddProperty( - "Email", - typeof(string), - propertyInfo: typeof(UserDto).GetProperty("Email", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - nullable: true); - - var fullName = runtimeEntityType.AddProperty( - "FullName", - typeof(string), - propertyInfo: typeof(UserDto).GetProperty("FullName", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - var gender = runtimeEntityType.AddProperty( - "Gender", - typeof(Gender?), - propertyInfo: typeof(UserDto).GetProperty("Gender", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - nullable: true); - - var hasProfilePicture = runtimeEntityType.AddProperty( - "HasProfilePicture", - typeof(bool), - propertyInfo: typeof(UserDto).GetProperty("HasProfilePicture", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - sentinel: false); - - var password = runtimeEntityType.AddProperty( - "Password", - typeof(string), - propertyInfo: typeof(UserDto).GetProperty("Password", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - var phoneNumber = runtimeEntityType.AddProperty( - "PhoneNumber", - typeof(string), - propertyInfo: typeof(UserDto).GetProperty("PhoneNumber", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), - nullable: true); - - var userName = runtimeEntityType.AddProperty( - "UserName", - typeof(string), - propertyInfo: typeof(UserDto).GetProperty("UserName", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), - fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - var key = runtimeEntityType.AddKey( - new[] { id }); - runtimeEntityType.SetPrimaryKey(key); - - return runtimeEntityType; - } - - public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) - { - runtimeEntityType.AddAnnotation("Relational:FunctionName", null); - runtimeEntityType.AddAnnotation("Relational:Schema", null); - runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); - runtimeEntityType.AddAnnotation("Relational:TableName", "Users"); - runtimeEntityType.AddAnnotation("Relational:ViewName", null); - runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); - - Customize(runtimeEntityType); - } - - static partial void Customize(RuntimeEntityType runtimeEntityType); + var runtimeEntityType = model.AddEntityType( + "Boilerplate.Shared.Dtos.Identity.UserDto", + typeof(UserDto), + baseEntityType, + propertyCount: 10, + keyCount: 1); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(Guid), + propertyInfo: typeof(UserDto).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw, + sentinel: new Guid("00000000-0000-0000-0000-000000000000")); + + var birthDate = runtimeEntityType.AddProperty( + "BirthDate", + typeof(DateTimeOffset?), + propertyInfo: typeof(UserDto).GetProperty("BirthDate", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true, + valueConverter: new DateTimeOffsetToBinaryConverter()); + + var email = runtimeEntityType.AddProperty( + "Email", + typeof(string), + propertyInfo: typeof(UserDto).GetProperty("Email", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + + var fullName = runtimeEntityType.AddProperty( + "FullName", + typeof(string), + propertyInfo: typeof(UserDto).GetProperty("FullName", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var gender = runtimeEntityType.AddProperty( + "Gender", + typeof(Gender?), + propertyInfo: typeof(UserDto).GetProperty("Gender", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + + var hasProfilePicture = runtimeEntityType.AddProperty( + "HasProfilePicture", + typeof(bool), + propertyInfo: typeof(UserDto).GetProperty("HasProfilePicture", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: false); + + var password = runtimeEntityType.AddProperty( + "Password", + typeof(string), + propertyInfo: typeof(UserDto).GetProperty("Password", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var phoneNumber = runtimeEntityType.AddProperty( + "PhoneNumber", + typeof(string), + propertyInfo: typeof(UserDto).GetProperty("PhoneNumber", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + + var userName = runtimeEntityType.AddProperty( + "UserName", + typeof(string), + propertyInfo: typeof(UserDto).GetProperty("UserName", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + var version = runtimeEntityType.AddProperty( + "Version", + typeof(string), + propertyInfo: typeof(UserDto).GetProperty("Version", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(UserDto).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + return runtimeEntityType; } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", null); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "Users"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20250416163233_InitialMigration.Designer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.Designer.cs similarity index 90% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20250416163233_InitialMigration.Designer.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.Designer.cs index c53063d127..35d22380f0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20250416163233_InitialMigration.Designer.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.Designer.cs @@ -10,15 +10,15 @@ namespace Boilerplate.Client.Core.Data.Migrations; -[DbContext(typeof(OfflineDbContext))] -[Migration("20250416163233_InitialMigration")] -partial class InitialMigration +[DbContext(typeof(AppOfflineDbContext))] +[Migration("20251128225322_Initial")] +partial class Initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.4"); + modelBuilder.HasAnnotation("ProductVersion", "10.0.0"); modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b => { @@ -29,9 +29,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("BirthDate") .HasColumnType("INTEGER"); - b.Property("ConcurrencyStamp") - .HasColumnType("TEXT"); - b.Property("Email") .HasColumnType("TEXT"); @@ -56,6 +53,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("TEXT"); + b.Property("Version") + .HasColumnType("TEXT"); + b.HasKey("Id"); b.ToTable("Users"); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20250416163233_InitialMigration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.cs similarity index 74% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20250416163233_InitialMigration.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.cs index 1cfa821df2..68d9459355 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20250416163233_InitialMigration.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.cs @@ -1,11 +1,12 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using System; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace Boilerplate.Client.Core.Data.Migrations; /// -public partial class InitialMigration : Migration +public partial class Initial : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -23,7 +24,7 @@ protected override void Up(MigrationBuilder migrationBuilder) Gender = table.Column(type: "INTEGER", nullable: true), BirthDate = table.Column(type: "INTEGER", nullable: true), HasProfilePicture = table.Column(type: "INTEGER", nullable: false), - ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) + Version = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -32,8 +33,8 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.InsertData( table: "Users", - columns: new[] { "Id", "BirthDate", "ConcurrencyStamp", "Email", "FullName", "Gender", "HasProfilePicture", "Password", "PhoneNumber", "UserName" }, - values: new object[] { new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), 1306790461440000000L, null, "test@bitplatform.dev", "Boilerplate test account", 0, false, "123456", "+31684207362", "test" }); + columns: new[] { "Id", "BirthDate", "Email", "FullName", "Gender", "HasProfilePicture", "Password", "PhoneNumber", "UserName", "Version" }, + values: new object[] { new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), 1306790461440000000L, "test@bitplatform.dev", "Boilerplate test account", 0, false, "123456", "+31684207362", "test", null }); } /// diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/AppOfflineDbContextModelSnapshot.cs similarity index 91% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/AppOfflineDbContextModelSnapshot.cs index 764b7e6d13..f3840ae87e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/AppOfflineDbContextModelSnapshot.cs @@ -9,13 +9,13 @@ namespace Boilerplate.Client.Core.Data.Migrations; -[DbContext(typeof(OfflineDbContext))] -partial class OfflineDbContextModelSnapshot : ModelSnapshot +[DbContext(typeof(AppOfflineDbContext))] +partial class AppOfflineDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.4"); + modelBuilder.HasAnnotation("ProductVersion", "10.0.0"); modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b => { @@ -26,9 +26,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("BirthDate") .HasColumnType("INTEGER"); - b.Property("ConcurrencyStamp") - .HasColumnType("TEXT"); - b.Property("Email") .HasColumnType("TEXT"); @@ -53,6 +50,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("TEXT"); + b.Property("Version") + .HasColumnType("TEXT"); + b.HasKey("Id"); b.ToTable("Users"); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/README.md b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/README.md index 1b4848a858..4795717a5f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/README.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/README.md @@ -21,14 +21,14 @@ https://inloop.github.io/sqlite-viewer/ **Migration** -`OfflineDbContext` migrations are slightly different from Boilerplate.Server.Api's `AppDbContext` migrations. -To add migration for `OfflineDbContext` first set `Boilerplate.Server.Web` as the Startup Project in solution explorer and set `Boilerplate.Client.Core` it as the Default Project in Package Manager Console and run the following commands: +`AppOfflineDbContext` migrations are slightly different from Boilerplate.Server.Api's `AppDbContext` migrations. +To add migration for `AppOfflineDbContext` first set `Boilerplate.Server.Web` as the Startup Project in solution explorer and set `Boilerplate.Client.Core` it as the Default Project in Package Manager Console and run the following commands: ```powershell -Add-Migration Initial -OutputDir Data\Migrations -Context OfflineDbContext -Verbose +Add-Migration Initial -OutputDir Data\Migrations -Context AppOfflineDbContext -Verbose ``` Or open a terminal in your Boilerplate.Server.Web project directory and run followings: ```bash -dotnet ef migrations add Initial --context OfflineDbContext --output-dir Data/Migrations --project ../Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj --verbose +dotnet ef migrations add Initial --context AppOfflineDbContext --output-dir Data/Migrations --project ../Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj --verbose ``` *Note*: If you encounter any problem in running these commands, first make sure that the solution builds successfully. @@ -45,8 +45,8 @@ To implement this optimization, follow these steps in the Package Manager Consol 2. Run the following command: - ```powershell - Optimize-DbContext -Context OfflineDbContext -OutputDir Data/CompiledModel -Namespace Boilerplate.Client.Core.Data -Verbose - ``` +```powershell +Optimize-DbContext -Context AppOfflineDbContext -OutputDir Data/CompiledModel -Namespace Boilerplate.Client.Core.Data -Verbose +``` By adhering to these steps, you leverage EF Core compiled models to boost the performance of your application, ensuring an optimized and efficient data access method. \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs index 9db11d637a..e30a4c47bd 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs @@ -108,7 +108,7 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle }); //#if (offlineDb == true) - services.AddBesqlDbContextFactory((sp, optionsBuilder) => + services.AddBesqlDbContextFactory((sp, optionsBuilder) => { var isRunningInsideDocker = Directory.Exists("/container_volume"); // Blazor Server - Docker (It's supposed to be a mounted volume named /container_volume) var dirPath = isRunningInsideDocker ? "/container_volume" @@ -119,7 +119,7 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle Directory.CreateDirectory(dirPath); - var dbPath = Path.Combine(dirPath, "Offline.db"); + var dbPath = Path.Combine(dirPath, "AppOffline.db"); optionsBuilder.UseSqlite($"Data Source={dbPath}", dbOptions => { @@ -128,7 +128,7 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle if (AppEnvironment.IsDevelopment() is false) { - optionsBuilder.UseModel(OfflineDbContextModel.Instance); + optionsBuilder.UseModel(AppOfflineDbContextModel.Instance); } optionsBuilder.EnableSensitiveDataLogging(AppEnvironment.IsDevelopment()) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props index 90fc0b8972..4c69d800a0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props @@ -5,7 +5,7 @@ enable true true - $(NoWarn);CS1998;CS1591;ASPIREPROXYENDPOINTS001 + $(NoWarn);CS1998;CS1591;ASPIREPROXYENDPOINTS001;RMG012;RMG020 $(WarningsAsErrors);CS0114;CS8785;RZ10012;ASP0018 1.0.0 diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs index 47437e3a4e..e48376f113 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs @@ -26,7 +26,7 @@ public IQueryable Get() } [HttpGet] - public async Task> GetCategories(ODataQueryOptions odataQuery, CancellationToken cancellationToken) + public async Task> GetCategories(ODataQueryOptions odataQuery, CancellationToken cancellationToken) { var query = (IQueryable)odataQuery.ApplyTo(Get(), ignoreQueryOptions: AllowedQueryOptions.Top | AllowedQueryOptions.Skip); @@ -35,7 +35,7 @@ public async Task> GetCategories(ODataQueryOptions(await query.ToArrayAsync(cancellationToken), totalCount); + return new PagedResponse(await query.ToArrayAsync(cancellationToken), totalCount); } [HttpGet("{id}")] @@ -86,15 +86,15 @@ public async Task Update(CategoryDto dto, CancellationToken cancell return entityToUpdate.Map(); } - [HttpDelete("{id}/{concurrencyStamp}")] - public async Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken) + [HttpDelete("{id}/{version}")] + public async Task Delete(Guid id, string version, CancellationToken cancellationToken) { if (await DbContext.Products.AnyAsync(p => p.CategoryId == id, cancellationToken)) { throw new BadRequestException(Localizer[nameof(AppStrings.CategoryNotEmpty)]); } - DbContext.Categories.Remove(new() { Id = id, ConcurrencyStamp = Convert.FromHexString(concurrencyStamp) }); + DbContext.Categories.Remove(new() { Id = id, Version = Convert.FromHexString(version) }); await DbContext.SaveChangesAsync(cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/RoleManagementController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/RoleManagementController.cs index 76cc237d00..aa54093f2b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/RoleManagementController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/RoleManagementController.cs @@ -62,7 +62,6 @@ public async Task Create(RoleDto roleDto, CancellationToken cancellatio { var role = roleDto.Map(); - role.ConcurrencyStamp = Guid.NewGuid().ToString(); var result = await roleManager.CreateAsync(role); if (result.Succeeded is false) @@ -80,9 +79,6 @@ public async Task Update(RoleDto roleDto, CancellationToken cancellatio if (AppRoles.IsBuiltInRole(role.Name!)) throw new BadRequestException(Localizer[nameof(AppStrings.CanNotChangeBuiltInRole), role.Name!]); - if (role.ConcurrencyStamp != roleDto.ConcurrencyStamp) - throw new ConflictException(); - roleDto.Patch(role); var result = await roleManager.UpdateAsync(role); @@ -93,18 +89,15 @@ public async Task Update(RoleDto roleDto, CancellationToken cancellatio return role.Map(); } - [HttpDelete("{roleId}/{concurrencyStamp}")] + [HttpDelete("{roleId}")] [Authorize(Policy = AuthPolicies.ELEVATED_ACCESS)] - public async Task Delete(Guid roleId, string concurrencyStamp, CancellationToken cancellationToken) + public async Task Delete(Guid roleId, CancellationToken cancellationToken) { var role = await GetRoleById(roleId, cancellationToken); if (AppRoles.IsBuiltInRole(role.Name!)) throw new BadRequestException(Localizer[nameof(AppStrings.CanNotChangeBuiltInRole), role.Name!]); - if (role.ConcurrencyStamp != concurrencyStamp) - throw new ConflictException(); - await roleManager.DeleteAsync(role); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs index 0ac2ae3d63..1a3b1a0f7a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs @@ -34,7 +34,7 @@ public IQueryable Get() } [HttpGet] - public async Task> GetProducts(ODataQueryOptions odataQuery, CancellationToken cancellationToken) + public async Task> GetProducts(ODataQueryOptions odataQuery, CancellationToken cancellationToken) { var query = (IQueryable)odataQuery.ApplyTo(Get(), ignoreQueryOptions: AllowedQueryOptions.Top | AllowedQueryOptions.Skip); @@ -43,11 +43,11 @@ public async Task> GetProducts(ODataQueryOptions(await query.ToArrayAsync(cancellationToken), totalCount); + return new PagedResponse(await query.ToArrayAsync(cancellationToken), totalCount); } [HttpGet("{searchQuery}")] - public async Task> SearchProducts(string searchQuery, ODataQueryOptions odataQuery, CancellationToken cancellationToken) + public async Task> SearchProducts(string searchQuery, ODataQueryOptions odataQuery, CancellationToken cancellationToken) { //#if (database == "PostgreSQL" || database == "SqlServer") var query = (IQueryable)odataQuery.ApplyTo((await (productEmbeddingService.SearchProducts(searchQuery, cancellationToken))).Project(), @@ -57,7 +57,7 @@ public async Task> SearchProducts(string searchQuery, OD query = query.SkipIf(odataQuery.Skip is not null, odataQuery.Skip?.Value) .TakeIf(odataQuery.Top is not null, odataQuery.Top?.Value); - return new PagedResult(await query.ToArrayAsync(cancellationToken), totalCount); + return new PagedResponse(await query.ToArrayAsync(cancellationToken), totalCount); //#else throw new NotImplementedException(); // Embedding based search is only implemented for PostgreSQL and SQL Server only. //#endif @@ -139,13 +139,13 @@ public async Task Update(ProductDto dto, CancellationToken cancellat return entityToUpdate.Map(); } - [HttpDelete("{id}/{concurrencyStamp}")] - public async Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken) + [HttpDelete("{id}/{version}")] + public async Task Delete(Guid id, string version, CancellationToken cancellationToken) { var entityToDelete = await DbContext.Products.FindAsync([id], cancellationToken) ?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.ProductCouldNotBeFound)]); - entityToDelete.ConcurrencyStamp = Convert.FromHexString(concurrencyStamp); + entityToDelete.Version = Convert.FromHexString(version); DbContext.Remove(entityToDelete); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs index 8fc50b2f4f..1db6dbd180 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs @@ -19,7 +19,7 @@ public IQueryable Get() } [HttpGet] - public async Task> GetTodoItems(ODataQueryOptions odataQuery, CancellationToken cancellationToken) + public async Task> GetTodoItems(ODataQueryOptions odataQuery, CancellationToken cancellationToken) { var query = (IQueryable)odataQuery.ApplyTo(Get(), ignoreQueryOptions: AllowedQueryOptions.Top | AllowedQueryOptions.Skip); @@ -28,7 +28,7 @@ public async Task> GetTodoItems(ODataQueryOptions(await query.ToArrayAsync(cancellationToken), totalCount); + return new PagedResponse(await query.ToArrayAsync(cancellationToken), totalCount); } [HttpGet("{id}")] @@ -47,7 +47,7 @@ public async Task Create(TodoItemDto dto, CancellationToken cancell entityToAdd.UserId = User.GetUserId(); - entityToAdd.Date = DateTimeOffset.UtcNow; + entityToAdd.UpdatedAt = DateTimeOffset.UtcNow; await DbContext.TodoItems.AddAsync(entityToAdd, cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs index 0b496ff445..9eaa69b537 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs @@ -133,8 +133,8 @@ private void SetConcurrencyStamp() foreach (var entityEntry in ChangeTracker.Entries().Where(e => e.State is EntityState.Modified or EntityState.Deleted)) { - if (entityEntry.CurrentValues.TryGetValue("ConcurrencyStamp", out var currentConcurrencyStamp) is false - || currentConcurrencyStamp is not byte[]) + if (entityEntry.CurrentValues.TryGetValue("Version", out var currentVersion) is false + || currentVersion is not byte[]) continue; //#if (database != "Sqlite") @@ -143,7 +143,7 @@ private void SetConcurrencyStamp() { //#endif // https://github.com/dotnet/efcore/issues/35443 - entityEntry.OriginalValues.SetValues(new Dictionary { { "ConcurrencyStamp", currentConcurrencyStamp } }); + entityEntry.OriginalValues.SetValues(new Dictionary { { "Version", currentVersion } }); //#if (IsInsideProjectTemplate == true) } //#endif @@ -152,7 +152,7 @@ private void SetConcurrencyStamp() if (Database.ProviderName!.EndsWith("Sqlite", StringComparison.InvariantCulture)) { //#endif - entityEntry.CurrentValues.SetValues(new Dictionary { { "ConcurrencyStamp", RandomNumberGenerator.GetBytes(8) } }); + entityEntry.CurrentValues.SetValues(new Dictionary { { "Version", RandomNumberGenerator.GetBytes(8) } }); //#if (IsInsideProjectTemplate == true) } //#endif @@ -231,7 +231,7 @@ private void ConfigureConcurrencyStamp(ModelBuilder modelBuilder) foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties() - .Where(p => p.Name is "ConcurrencyStamp" && p.PropertyInfo?.PropertyType == typeof(byte[]))) + .Where(p => p.Name is "Version" && p.PropertyInfo?.PropertyType == typeof(byte[]))) { var builder = new PropertyBuilder(property); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Category/CategoryConfiguration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Category/CategoryConfiguration.cs index a37af143d7..40a8fccac4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Category/CategoryConfiguration.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Category/CategoryConfiguration.cs @@ -8,13 +8,13 @@ public void Configure(EntityTypeBuilder builder) { builder.HasIndex(p => p.Name).IsUnique(); - var defaultConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + var defaultVersion = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; builder.HasData( - new Category { Id = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), Name = "Ford", Color = "#FFCD56", ConcurrencyStamp = defaultConcurrencyStamp }, - new Category { Id = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), Name = "Nissan", Color = "#FF6384", ConcurrencyStamp = defaultConcurrencyStamp }, - new Category { Id = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), Name = "Benz", Color = "#4BC0C0", ConcurrencyStamp = defaultConcurrencyStamp }, - new Category { Id = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), Name = "BMW", Color = "#FF9124", ConcurrencyStamp = defaultConcurrencyStamp }, - new Category { Id = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d"), Name = "Tesla", Color = "#2B88D8", ConcurrencyStamp = defaultConcurrencyStamp }); + new Category { Id = Guid.Parse("31d78bd0-0b4f-4e87-b02f-8f66d4ab2845"), Name = "Ford", Color = "#FFCD56", Version = defaultVersion }, + new Category { Id = Guid.Parse("582b8c19-0709-4dae-b7a6-fa0e704dad3c"), Name = "Nissan", Color = "#FF6384", Version = defaultVersion }, + new Category { Id = Guid.Parse("6fae78f3-b067-40fb-a2d5-9c8dd5eb2e08"), Name = "Benz", Color = "#4BC0C0", Version = defaultVersion }, + new Category { Id = Guid.Parse("ecf0496f-f1e3-4d92-8fe4-0d7fa2b4ffa4"), Name = "BMW", Color = "#FF9124", Version = defaultVersion }, + new Category { Id = Guid.Parse("747f6d66-7524-40ca-8494-f65e85b5ee5d"), Name = "Tesla", Color = "#2B88D8", Version = defaultVersion }); } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Chatbot/SystemPromptConfiguration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Chatbot/SystemPromptConfiguration.cs index 43857941fc..f94010d2e9 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Chatbot/SystemPromptConfiguration.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Chatbot/SystemPromptConfiguration.cs @@ -9,13 +9,13 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(sp => sp.PromptKind) .IsUnique(); - var defaultConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + var defaultVersion = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; builder.HasData(new SystemPrompt { Id = Guid.Parse("a8c94d94-0004-4dd0-921c-255e0a581424"), PromptKind = PromptKind.Support, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = defaultVersion, Markdown = GetInitialSystemPromptMarkdown() }); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs index e42919df8c..41709de7b5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs @@ -34,7 +34,7 @@ public void Configure(EntityTypeBuilder builder) builder.Ignore(p => p.Embedding); //#endif - var defaultConcurrencyStamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + var version = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; DateTimeOffset baseDate = DateTimeOffset.Parse("2022-07-12", styles: DateTimeStyles.AssumeUniversal); // --- Benz Entries (19 cars) --- @@ -51,7 +51,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10000, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -65,7 +65,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10001, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -79,7 +79,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10003, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -93,7 +93,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10004, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -107,7 +107,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10005, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -121,7 +121,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10006, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -135,7 +135,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10007, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -149,7 +149,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10008, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -163,7 +163,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10009, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -177,7 +177,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10010, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -191,7 +191,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10011, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -205,7 +205,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10012, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -219,7 +219,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10013, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -233,7 +233,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10014, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -247,7 +247,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10015, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -261,7 +261,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10016, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -275,7 +275,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10017, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -289,7 +289,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10018, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -303,7 +303,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = benzId, ShortId = 10019, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -320,7 +320,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = fordId, ShortId = 10020, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -334,7 +334,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = fordId, ShortId = 10021, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -348,7 +348,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = fordId, ShortId = 10022, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -362,7 +362,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = fordId, ShortId = 10023, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -376,7 +376,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = fordId, ShortId = 10024, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -390,7 +390,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = fordId, ShortId = 10025, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -404,7 +404,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = fordId, ShortId = 10026, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -418,7 +418,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = fordId, ShortId = 10027, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -436,7 +436,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = nissanId, ShortId = 10028, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -450,7 +450,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = nissanId, ShortId = 10029, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -464,7 +464,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = nissanId, ShortId = 10030, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -478,7 +478,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = nissanId, ShortId = 10031, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -492,7 +492,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = nissanId, ShortId = 10032, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -506,7 +506,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = nissanId, ShortId = 10033, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -520,7 +520,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = nissanId, ShortId = 10034, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -534,7 +534,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = nissanId, ShortId = 10035, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -551,7 +551,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = bmwId, ShortId = 10036, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -565,7 +565,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = bmwId, ShortId = 10037, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -579,7 +579,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = bmwId, ShortId = 10038, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -593,7 +593,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = bmwId, ShortId = 10039, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -607,7 +607,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = bmwId, ShortId = 10040, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -621,7 +621,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = bmwId, ShortId = 10041, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -635,7 +635,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = bmwId, ShortId = 10042, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -649,7 +649,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = bmwId, ShortId = 10043, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -666,7 +666,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = teslaId, ShortId = 10044, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -680,7 +680,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = teslaId, ShortId = 10045, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -694,7 +694,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = teslaId, ShortId = 10046, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -708,7 +708,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = teslaId, ShortId = 10047, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -722,7 +722,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = teslaId, ShortId = 10048, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -736,7 +736,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = teslaId, ShortId = 10049, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); @@ -750,7 +750,7 @@ public void Configure(EntityTypeBuilder builder) CreatedOn = baseDate.AddDays(-10), CategoryId = teslaId, ShortId = 10050, - ConcurrencyStamp = defaultConcurrencyStamp, + Version = version, HasPrimaryImage = false }); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/IdentityMapper.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/IdentityMapper.cs index 7bd011a3c5..cb4a1d33e5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/IdentityMapper.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/IdentityMapper.cs @@ -10,26 +10,31 @@ namespace Boilerplate.Server.Api.Mappers; [Mapper] public static partial class IdentityMapper { + public static partial RoleDto Map(this Role source); + public static partial Role Map(this RoleDto source); + public static partial void Patch(this RoleDto source, Role destination); + public static partial IQueryable Project(this IQueryable query); + + + + [MapProperty(nameof(@User.ConcurrencyStamp), nameof(@UserDto.Version))] public static partial UserDto Map(this User source); public static partial User Map(this UserDto source); public static partial void Patch(this UserDto source, User destination); public static partial void Patch(this EditUserRequestDto source, User destination); + public static partial IQueryable Project(this IQueryable query); - [MapPropertyFromSource(nameof(UserSessionDto.RenewedOn), Use = nameof(MapRenewedOn))] - public static partial IQueryable Project(this IQueryable source); - [MapPropertyFromSource(nameof(UserSessionDto.RenewedOn), Use = nameof(MapRenewedOn))] + + [MapPropertyFromSource(nameof(@UserSessionDto.RenewedOn), Use = nameof(MapRenewedOn))] public static partial UserSessionDto Map(this UserSession source); + public static partial IQueryable Project(this IQueryable source); + + [UserMapping] + private static long MapRenewedOn(UserSession us) => us.RenewedOn ?? us.StartedOn; + - public static partial RoleDto Map(this Role source); - public static partial Role Map(this RoleDto source); - public static partial void Patch(this RoleDto source, Role destination); - public static partial IQueryable Project(this IQueryable query); - public static partial IQueryable Project(this IQueryable query); public static partial ClaimDto Map(this RoleClaim source); public static partial IQueryable Project(this IQueryable query); - - [UserMapping] - private static long MapRenewedOn(UserSession us) => us.RenewedOn ?? us.StartedOn; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Categories/Category.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Categories/Category.cs index 26f942d84a..f9ab43615b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Categories/Category.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Categories/Category.cs @@ -1,4 +1,4 @@ -using Boilerplate.Server.Api.Models.Products; +using Boilerplate.Server.Api.Models.Products; namespace Boilerplate.Server.Api.Models.Categories; @@ -11,7 +11,7 @@ public partial class Category public string? Color { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; public IList Products { get; set; } = []; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Chatbot/SystemPrompt.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Chatbot/SystemPrompt.cs index a8d15a210c..a712a26633 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Chatbot/SystemPrompt.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Chatbot/SystemPrompt.cs @@ -9,5 +9,5 @@ public class SystemPrompt [Required] public string? Markdown { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs index 46e3ae4e2e..ff7d288be2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs @@ -35,7 +35,7 @@ public partial class Product public Guid CategoryId { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; public bool HasPrimaryImage { get; set; } = false; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs index 45c1e2833c..366a214849 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs @@ -1,4 +1,4 @@ -using Boilerplate.Server.Api.Models.Identity; +using Boilerplate.Server.Api.Models.Identity; namespace Boilerplate.Server.Api.Models.Todo; @@ -8,7 +8,7 @@ public partial class TodoItem [Required] public string? Title { get; set; } - public DateTimeOffset Date { get; set; } + public DateTimeOffset UpdatedAt { get; set; } public bool IsDone { get; set; } [ForeignKey(nameof(UserId))] diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs index a1f5c9fd49..5bb6776650 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs @@ -1,4 +1,4 @@ -//+:cnd:noEmit +//+:cnd:noEmit using Boilerplate.Shared.Dtos.Categories; namespace Boilerplate.Shared.Controllers.Categories; @@ -13,7 +13,7 @@ public interface ICategoryController : IAppController Task Get(Guid id, CancellationToken cancellationToken); [HttpGet] - Task> GetCategories(CancellationToken cancellationToken) => default!; + Task> GetCategories(CancellationToken cancellationToken) => default!; [HttpGet] Task> Get(CancellationToken cancellationToken) => default!; @@ -25,7 +25,7 @@ public interface ICategoryController : IAppController [HttpPut] Task Update(CategoryDto dto, CancellationToken cancellationToken); - [HttpDelete("{id}/{concurrencyStamp}")] - Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken); + [HttpDelete("{id}/{version}")] + Task Delete(Guid id, string version, CancellationToken cancellationToken); //#endif } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IRoleManagementController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IRoleManagementController.cs index 21aa329970..35d8a72468 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IRoleManagementController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IRoleManagementController.cs @@ -24,8 +24,8 @@ public interface IRoleManagementController : IAppController [HttpPost] Task Update(RoleDto roleDto, CancellationToken cancellationToken); - [HttpDelete("{roleId}/{concurrencyStamp}")] - Task Delete(Guid roleId, string concurrencyStamp, CancellationToken cancellationToken); + [HttpDelete("{roleId}")] + Task Delete(Guid roleId, CancellationToken cancellationToken); [HttpPost("{roleId}")] Task AddClaims(Guid roleId, List roleClaims, CancellationToken cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs index baa98a1d1b..ded062686d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs @@ -11,10 +11,10 @@ public interface IProductController : IAppController Task> Get(CancellationToken cancellationToken) => default!; [HttpGet] - Task> GetProducts(CancellationToken cancellationToken) => default!; + Task> GetProducts(CancellationToken cancellationToken) => default!; [HttpGet("{searchQuery}")] - Task> SearchProducts(string searchQuery, CancellationToken cancellationToken) => default!; + Task> SearchProducts(string searchQuery, CancellationToken cancellationToken) => default!; [HttpGet("{id}")] Task Get(Guid id, CancellationToken cancellationToken); @@ -25,6 +25,6 @@ public interface IProductController : IAppController [HttpPut] Task Update(ProductDto dto, CancellationToken cancellationToken); - [HttpDelete("{id}/{concurrencyStamp}")] - Task Delete(Guid id, string concurrencyStamp, CancellationToken cancellationToken); + [HttpDelete("{id}/{version}")] + Task Delete(Guid id, string version, CancellationToken cancellationToken); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs index b228c9cfbf..5f7370ccd9 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs @@ -42,16 +42,16 @@ namespace Boilerplate.Shared.Dtos; //#endif //#if (sample == true) [JsonSerializable(typeof(TodoItemDto))] -[JsonSerializable(typeof(PagedResult))] +[JsonSerializable(typeof(PagedResponse))] [JsonSerializable(typeof(List))] //#endif //#if (module == "Admin" || module == "Sales") [JsonSerializable(typeof(CategoryDto))] [JsonSerializable(typeof(List))] -[JsonSerializable(typeof(PagedResult))] +[JsonSerializable(typeof(PagedResponse))] [JsonSerializable(typeof(ProductDto))] [JsonSerializable(typeof(List))] -[JsonSerializable(typeof(PagedResult))] +[JsonSerializable(typeof(PagedResponse))] //#endif //#if (module == "Admin") [JsonSerializable(typeof(List))] diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs index f84aefb91c..2738e1d08b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs @@ -1,4 +1,4 @@ -namespace Boilerplate.Shared.Dtos.Categories; +namespace Boilerplate.Shared.Dtos.Categories; [DtoResourceType(typeof(AppStrings))] public partial class CategoryDto @@ -15,5 +15,5 @@ public partial class CategoryDto public int ProductsCount { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Chatbot/SystemPromptDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Chatbot/SystemPromptDto.cs index 016d40687a..1456c975e2 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Chatbot/SystemPromptDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Chatbot/SystemPromptDto.cs @@ -7,5 +7,5 @@ public class SystemPromptDto [Required] public string? Markdown { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/RoleDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/RoleDto.cs index e3b411bb71..5fcee1bd53 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/RoleDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/RoleDto.cs @@ -9,6 +9,4 @@ public partial class RoleDto public string? Name { get; set; } public string? NormalizedName { get; set; } - - public string? ConcurrencyStamp { get; set; } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs index 6322673771..bebfc4b71c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs @@ -33,7 +33,7 @@ public partial class UserDto : IValidatableObject public bool HasProfilePicture { get; set; } - public string? ConcurrencyStamp { get; set; } + public string? Version { get; set; } public string? DisplayName => FullName ?? DisplayUserName; public string? DisplayUserName => Email ?? PhoneNumber ?? UserName; @@ -42,7 +42,7 @@ public partial class UserDto : IValidatableObject { return HasProfilePicture is false ? null - : new Uri(absoluteServerAddress, $"/api/Attachment/GetAttachment/{Id}/{AttachmentKind.UserProfileImageSmall}?v={ConcurrencyStamp}").ToString(); + : new Uri(absoluteServerAddress, $"/api/Attachment/GetAttachment/{Id}/{AttachmentKind.UserProfileImageSmall}?v={Version}").ToString(); } public IEnumerable Validate(ValidationContext validationContext) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PagedResultDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PagedResponse.cs similarity index 58% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PagedResultDto.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PagedResponse.cs index 8956a146c9..23fee0cfa5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PagedResultDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/PagedResponse.cs @@ -1,13 +1,13 @@ -namespace Boilerplate.Shared.Dtos; +namespace Boilerplate.Shared.Dtos; -public partial class PagedResult +public partial class PagedResponse { public T[] Items { get; set; } = []; public long TotalCount { get; set; } [JsonConstructor] - public PagedResult(T[] items, long totalCount) + public PagedResponse(T[] items, long totalCount) { Items = items; TotalCount = totalCount; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs index 5caa992115..52178f99e9 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs @@ -36,7 +36,7 @@ public partial class ProductDto [Display(Name = nameof(AppStrings.Category))] public string? CategoryName { get; set; } - public byte[] ConcurrencyStamp { get; set; } = []; + public byte[] Version { get; set; } = []; public bool HasPrimaryImage { get; set; } = false; @@ -47,7 +47,7 @@ public partial class ProductDto { return HasPrimaryImage is false ? null - : new Uri(absoluteServerAddress, $"/api/Attachment/GetAttachment/{Id}/{AttachmentKind.ProductPrimaryImageMedium}?v={ConcurrencyStamp.ToStampString()}").ToString(); + : new Uri(absoluteServerAddress, $"/api/Attachment/GetAttachment/{Id}/{AttachmentKind.ProductPrimaryImageMedium}?v={Version.ToStampString()}").ToString(); } public string FormattedPrice => FormatPrice(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs index 4041039244..335f2af5a5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs @@ -1,4 +1,4 @@ -namespace Boilerplate.Shared.Dtos.Todo; +namespace Boilerplate.Shared.Dtos.Todo; [DtoResourceType(typeof(AppStrings))] public partial class TodoItemDto @@ -9,7 +9,7 @@ public partial class TodoItemDto [Display(Name = nameof(AppStrings.Title))] public string? Title { get; set; } - public DateTimeOffset Date { get; set; } + public DateTimeOffset UpdatedAt { get; set; } public bool IsDone { get; set; } From 39c261be6e3b8a2cc6f886ce6456250e3dbd24ec Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Sat, 29 Nov 2025 12:49:20 +0330 Subject: [PATCH 27/39] feat(templates): add doc for OwningComponentBase to bit Boilerplate #11768 (#11767) --- ...ndency Injection & Service Registration.md | 46 +++++++++++++++++++ .../Components/AppClientCoordinator.cs | 4 +- .../Components/Pages/TodoPage.razor.cs | 14 ++---- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/09- Dependency Injection & Service Registration.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/09- Dependency Injection & Service Registration.md index 1e6b43b5c8..3ff858322c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/09- Dependency Injection & Service Registration.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/09- Dependency Injection & Service Registration.md @@ -250,6 +250,52 @@ public partial class FeedbackPage : AppPageBase } ``` +## Owned services + +### Default Service Lifetime in Blazor Components + +By default, services injected in Blazor components remain tied to the application scope for the entire lifetime: + +- **Blazor Server**: Until the user closes the browser tab or the browser gets disconnected. +- **Blazor WebAssembly / Blazor Hybrid**: Until the browser tab or app is closed. + +This is perfectly fine for most services (especially singletons or stateless ones), but services that hold resources (timers, event subscriptions, native handlers, etc.) may need to be disposed when their associated component is destroyed. + +### Using ScopedServices for Automatic Disposal + +To achieve automatic disposal when the component is disposed, inject the service via `ScopedServices` instead of using `[AutoInject]`. This creates a scoped service instance that gets disposed along with the component. + +**Example:** + +```csharp +Keyboard keyboard => field ??= ScopedServices.GetRequiredService(); // ??= means the service gets resolved when accessed, results into better performance. + +protected override async Task OnAfterFirstRenderAsync() +{ + await keyboard.Add(ButilKeyCodes.KeyF, () => searchBox.FocusAsync(), ButilModifiers.Ctrl); // Handles keyboard shortcuts + + await base.OnAfterFirstRenderAsync(); +} +``` + +Instead of + +```csharp +[AutoInject] private Keyboard keyboard = default!; + +protected override async Task OnAfterFirstRenderAsync() +{ + await keyboard.Add(ButilKeyCodes.KeyF, () => searchBox.FocusAsync(), ButilModifiers.Ctrl); // Handles keyboard shortcuts + + await base.OnAfterFirstRenderAsync(); +} + +protected override async ValueTask DisposeAsync(bool disposing) +{ + await keyboard.DisposeAsync(); + await base.DisposeAsync(disposing); +} +``` --- ### AI Wiki: Answered Questions diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs index a2226521ed..6ddb844e96 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppClientCoordinator.cs @@ -33,11 +33,9 @@ public partial class AppClientCoordinator : AppComponentBase [AutoInject] private UserAgent userAgent = default!; [AutoInject] private IJSRuntime jsRuntime = default!; [AutoInject] private IUserController userController = default!; - [AutoInject] private IStorageService storageService = default!; [AutoInject] private ILogger authLogger = default!; [AutoInject] private ILogger navigatorLogger = default!; [AutoInject] private ILogger logger = default!; - [AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!; //#if (notification == true) [AutoInject] private IPushNotificationService pushNotificationService = default!; //#endif @@ -362,7 +360,7 @@ private async Task ConfigureUISetup() if (CultureInfoManager.InvariantGlobalization is false) { CultureInfoManager.SetCurrentCulture(new Uri(NavigationManager.Uri).GetCulture() ?? // 1- Culture query string OR Route data request culture - await storageService.GetItem("Culture") ?? // 2- User settings + await StorageService.GetItem("Culture") ?? // 2- User settings CultureInfo.CurrentUICulture.Name); // 3- OS settings } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor.cs index 4943e9244c..65cff8d402 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor.cs @@ -5,9 +5,11 @@ namespace Boilerplate.Client.Core.Components.Pages; public partial class TodoPage { - [AutoInject] Keyboard keyboard = default!; [AutoInject] ITodoItemController todoItemController = default!; + // Refer to .docs/09- Dependency Injection & Service Registration.md 's Owned services section for more information about ScopedServices + Keyboard keyboard => field ??= ScopedServices.GetRequiredService(); + private bool isLoading; private string? searchText; private string? selectedSort; @@ -193,14 +195,4 @@ private async Task UpdateTodoItem(TodoItemDto todoItem) viewTodoItems.Remove(todoItem); } } - - protected override async ValueTask DisposeAsync(bool disposing) - { - await base.DisposeAsync(true); - - if (disposing) - { - await keyboard.DisposeAsync(); - } - } } From 6a77c7e5f957d716bd593242b6a59b756afc28f4 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sat, 29 Nov 2025 14:49:19 +0330 Subject: [PATCH 28/39] feat(blazorui): add missing tests of BitRichTextEditor component #11769 (#11770) --- .../RichTextEditor/BitRichTextEditor.razor.cs | 2 +- .../BitRichTextEditorJsRuntimeExtensions.cs | 5 + .../RichTextEditor/BitRichTextEditorTests.cs | 151 ++++++++++++++++++ 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/RichTextEditor/BitRichTextEditorTests.cs diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/RichTextEditor/BitRichTextEditor.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/RichTextEditor/BitRichTextEditor.razor.cs index 5caad3ce86..7da19f6d0f 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/RichTextEditor/BitRichTextEditor.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/RichTextEditor/BitRichTextEditor.razor.cs @@ -209,7 +209,7 @@ protected override async ValueTask DisposeAsync(bool disposing) try { - await _js.BitMarkdownEditorDispose(_Id); + await _js.BitRichTextEditorDispose(_Id); } catch (JSDisconnectedException) { } // we can ignore this exception here diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/RichTextEditor/BitRichTextEditorJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/RichTextEditor/BitRichTextEditorJsRuntimeExtensions.cs index 521dfbedf1..7aeb42c369 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/RichTextEditor/BitRichTextEditorJsRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/RichTextEditor/BitRichTextEditorJsRuntimeExtensions.cs @@ -48,4 +48,9 @@ public static ValueTask BitRichTextEditorSetContent(this IJSRuntime jsRuntime, s { return jsRuntime.InvokeVoid("BitBlazorUI.RichTextEditor.setContent", id, content); } + + public static ValueTask BitRichTextEditorDispose(this IJSRuntime jsRuntime, string id) + { + return jsRuntime.InvokeVoid("BitBlazorUI.RichTextEditor.dispose", id); + } } diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/RichTextEditor/BitRichTextEditorTests.cs b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/RichTextEditor/BitRichTextEditorTests.cs new file mode 100644 index 0000000000..cfb87309a1 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Components/Extras/RichTextEditor/BitRichTextEditorTests.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bit.BlazorUI.Tests.Components.Extras.RichTextEditor; + +[TestClass] +public class BitRichTextEditorTests : BunitTestContext +{ + private void SetupJsInterop() + { + Context.JSInterop.SetupVoid("BitBlazorUI.Extras.initScripts"); + Context.JSInterop.SetupVoid("BitBlazorUI.Extras.initStylesheets"); + Context.JSInterop.SetupVoid("BitBlazorUI.RichTextEditor.setup"); + Context.JSInterop.SetupVoid("BitBlazorUI.RichTextEditor.setText"); + Context.JSInterop.SetupVoid("BitBlazorUI.RichTextEditor.setHtml"); + Context.JSInterop.SetupVoid("BitBlazorUI.RichTextEditor.setContent"); + Context.JSInterop.Setup("BitBlazorUI.RichTextEditor.getText", inv => inv.Identifier == "BitBlazorUI.RichTextEditor.getText").SetResult("text"); + Context.JSInterop.Setup("BitBlazorUI.RichTextEditor.getHtml", inv => inv.Identifier == "BitBlazorUI.RichTextEditor.getHtml").SetResult("

html

"); + Context.JSInterop.Setup("BitBlazorUI.RichTextEditor.getContent", inv => inv.Identifier == "BitBlazorUI.RichTextEditor.getContent").SetResult("{ content: true }"); + Context.JSInterop.SetupVoid("BitBlazorUI.RichTextEditor.dispose"); + } + + [TestMethod] + public void BitRichTextEditorShouldRenderEditorAndToolbar() + { + SetupJsInterop(); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.ToolbarTemplate, b => b.AddContent(0, "Toolbar")); + parameters.Add(p => p.EditorTemplate, b => b.AddContent(1, "Editor")); + }); + + var editor = component.Find(".bit-rte-edt"); + var toolbar = component.Find(".bit-rte-tlb"); + + Assert.AreEqual("Editor", editor.TextContent); + Assert.AreEqual("Toolbar", toolbar.TextContent); + } + + [TestMethod] + public void BitRichTextEditorShouldApplyClassesAndReversed() + { + SetupJsInterop(); + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.Classes, new BitRichTextEditorClassStyles + { + Editor = "custom-editor", + Toolbar = "custom-toolbar", + Root = "custom-root" + }); + parameters.Add(p => p.Reversed, true); + }); + + var root = component.Find(".bit-rte"); + Assert.IsTrue(root.ClassList.Contains("custom-root")); + Assert.IsTrue(root.ClassList.Contains("bit-rte-rvs")); + Assert.IsTrue(component.Find(".bit-rte-edt").ClassList.Contains("custom-editor")); + //Assert.IsTrue(component.Find(".bit-rte-tlb").ClassList.Contains("custom-toolbar")); + } + + [TestMethod] + public async Task BitRichTextEditorShouldInvokeOnEditorReady() + { + SetupJsInterop(); + + var readyCalled = false; + + var component = RenderComponent(parameters => + { + parameters.Add(p => p.OnEditorReady, EventCallback.Factory.Create(this, _ => readyCalled = true)); + }); + + await component.Instance.GetText(); // ensure ready + + component.WaitForAssertion(() => Assert.IsTrue(readyCalled)); + } + + [TestMethod] + public async Task BitRichTextEditorShouldGetAndSetValues() + { + SetupJsInterop(); + + var component = RenderComponent(); + + var text = await component.Instance.GetText(); + var html = await component.Instance.GetHtml(); + var content = await component.Instance.GetContent(); + + Assert.AreEqual("text", text); + Assert.AreEqual("

html

", html); + Assert.AreEqual("{ content: true }", content); + + await component.Instance.SetText("new text"); + await component.Instance.SetHtml("

new html

"); + await component.Instance.SetContent("{ data: true }"); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.RichTextEditor.setText"); + Context.JSInterop.VerifyInvoke("BitBlazorUI.RichTextEditor.setHtml"); + Context.JSInterop.VerifyInvoke("BitBlazorUI.RichTextEditor.setContent"); + } + + [TestMethod] + public void BitRichTextEditorShouldLoadModules() + { + SetupJsInterop(); + + var module = new BitRichTextEditorModule { Name = "mentions", Src = "/mention.js", Config = new { } }; + + RenderComponent(parameters => + { + parameters.Add(p => p.Modules, new List { module }); + }); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.Extras.initScripts", 2); + } + + [TestMethod] + public void BitRichTextEditorShouldApplyThemeAndPlaceholder() + { + SetupJsInterop(); + + RenderComponent(parameters => + { + parameters.Add(p => p.Theme, BitRichTextEditorTheme.Bubble); + parameters.Add(p => p.Placeholder, "Type here"); + parameters.Add(p => p.ReadOnly, true); + parameters.Add(p => p.FullToolbar, true); + }); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.Extras.initStylesheets"); + Context.JSInterop.VerifyInvoke("BitBlazorUI.RichTextEditor.setup"); + } + + [TestMethod] + public async Task BitRichTextEditorShouldDisposeJsInterop() + { + SetupJsInterop(); + + var component = RenderComponent(); + + await component.Instance.DisposeAsync(); + + Context.JSInterop.VerifyInvoke("BitBlazorUI.RichTextEditor.dispose"); + } +} From 4717b4e30f375656562713121f4a1ec35ad52b75 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 30 Nov 2025 10:37:03 +0330 Subject: [PATCH 29/39] feat(blazorui): remove hot-reload related settings from the vs settings file of BlazorUI #11773 (#11774) --- src/BlazorUI/settings.VisualStudio.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/BlazorUI/settings.VisualStudio.json b/src/BlazorUI/settings.VisualStudio.json index d92d4093bb..65391ae629 100644 --- a/src/BlazorUI/settings.VisualStudio.json +++ b/src/BlazorUI/settings.VisualStudio.json @@ -4,11 +4,7 @@ "languages.defaults.general.lineNumbers": true, "environment.documents.saveWithSpecificEncoding": true, "environment.documents.saveEncoding": "utf-8-nobom;65001", - "debugging.hotReload.enableHotReload": true, - "debugging.hotReload.applyOnFileSave": true, "debugging.general.disableJITOptimization": true, - "debugging.hotReload.enableForNoDebugLaunch": true, - "projectsAndSolutions.aspNetCore.general.hotReloadCssChanges": true, "copilot.general.completions.enableNextEditSuggestions": true, "copilot.general.editor.enableAdaptivePaste": true, "copilot.general.chat.enablePlanning": true, From 946e92688dfcba4ab0a1508362e6719cd97724a3 Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Sun, 30 Nov 2025 11:58:54 +0330 Subject: [PATCH 30/39] feat(blazorui): add missing TabIndex parameter to BitComponentBase #11771 (#11772) --- .../Bit.BlazorUI/Components/BitComponentBase.cs | 13 ++++++++++++- .../BitCircularTimePicker.razor.cs | 5 ----- .../Inputs/DatePicker/BitDatePicker.razor.cs | 5 ----- .../DateRangePicker/BitDateRangePicker.razor.cs | 5 ----- .../Inputs/TextField/BitTextField.razor.cs | 5 ----- .../Inputs/TimePicker/BitTimePicker.razor.cs | 5 ----- .../Components/DemoPage.razor.cs | 7 +++++++ .../BitCircularTimePickerDemo.razor.cs | 7 ------- .../Inputs/DatePicker/BitDatePickerDemo.razor.cs | 7 ------- .../DateRangePicker/BitDateRangePickerDemo.razor.cs | 7 ------- .../Inputs/TextField/BitTextFieldDemo.razor.cs | 7 ------- .../Inputs/TimePicker/BitTimePickerDemo.razor.cs | 7 ------- 12 files changed, 19 insertions(+), 61 deletions(-) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs b/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs index 2d6a35e90a..83893fb2e8 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs @@ -3,7 +3,7 @@ public abstract partial class BitComponentBase : ComponentBase, IAsyncDisposable { private BitDir? _dir; - private string _uniqueId = BitShortId.NewId(); + private readonly string _uniqueId = BitShortId.NewId(); protected bool IsRendered; protected bool IsDisposed; @@ -69,6 +69,11 @@ public BitDir? Dir /// [Parameter] public string? Style { get; set; } + /// + /// The value of the tabindex html attribute of the element. + /// + [Parameter] public string? TabIndex { get; set; } + /// /// Whether the component is visible, hidden or collapsed. /// @@ -129,6 +134,12 @@ public override Task SetParametersAsync(ParameterView parameters) parametersDictionary.Remove(parameter.Key); break; + case nameof(TabIndex): + var tabindex = (string?)parameter.Value; + TabIndex = tabindex; + parametersDictionary.Remove(parameter.Key); + break; + case nameof(Visibility): var visibility = (BitVisibility)parameter.Value; if (Visibility != visibility) StyleBuilder.Reset(); diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs index d7cf256584..17f22ef1d4 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs @@ -165,11 +165,6 @@ public partial class BitCircularTimePicker : BitInputBase [Parameter, ResetClassBuilder] public bool Standalone { get; set; } - /// - /// The tabIndex of the TextField. - /// - [Parameter] public int TabIndex { get; set; } - /// /// The time format of the time-picker, 24H or 12H. /// diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs index df33f7aa5c..89eb63ce74 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs @@ -362,11 +362,6 @@ private int _minuteView /// [Parameter] public BitDatePickerClassStyles? Styles { get; set; } - /// - /// The tabIndex of the DatePicker's input. - /// - [Parameter] public int TabIndex { get; set; } - /// /// The time format of the time-picker, 24H or 12H. /// diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs index 6cf97dfdc6..2ecdd960d9 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs @@ -414,11 +414,6 @@ private int _endTimeMinuteView /// [Parameter] public BitDateRangePickerClassStyles? Styles { get; set; } - /// - /// The tabIndex of the DateRangePicker's input. - /// - [Parameter] public int TabIndex { get; set; } - /// /// Time format of the time-pickers, 24H or 12H. /// diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs index b8b443b52b..aea46d845b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs @@ -203,11 +203,6 @@ public partial class BitTextField : BitTextInputBase /// [Parameter] public RenderFragment? SuffixTemplate { get; set; } - /// - /// The value of the tabindex html attribute of the input element. - /// - [Parameter] public string? TabIndex { get; set; } - /// /// Specifies whether to remove any leading or trailing whitespace from the value. /// diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs index 3f86bdc0d6..502ff2fc7a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs @@ -236,11 +236,6 @@ private string? _minuteView [Parameter, ResetClassBuilder] public bool Standalone { get; set; } - /// - /// The tabIndex of the TextField. - /// - [Parameter] public int TabIndex { get; set; } - /// /// The time format of the time-picker, 24H or 12H. /// diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/DemoPage.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/DemoPage.razor.cs index 4c4f4e4efd..88322f8f7d 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/DemoPage.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/DemoPage.razor.cs @@ -79,6 +79,13 @@ public partial class DemoPage Description = "Custom CSS style for the root element of the component.", }, new() + { + Name = "TabIndex", + Type = "string?", + DefaultValue = "null", + Description = "The value of the tabindex html attribute of the element.", + }, + new() { Name = "Visibility", Type = "BitVisibility", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/CircularTimePicker/BitCircularTimePickerDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/CircularTimePicker/BitCircularTimePickerDemo.razor.cs index ab484726f0..2e03e558ab 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/CircularTimePicker/BitCircularTimePickerDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/CircularTimePicker/BitCircularTimePickerDemo.razor.cs @@ -190,13 +190,6 @@ public partial class BitCircularTimePickerDemo Description = "Whether the TimePicker is rendered standalone or with the input component and callout.", }, new() - { - Name = "TabIndex", - Type = "int", - DefaultValue = "0", - Description = "The tabIndex of the TextField.", - }, - new() { Name = "TimeFormat", Type = "BitTimeFormat", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/DatePicker/BitDatePickerDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/DatePicker/BitDatePickerDemo.razor.cs index 61116621b9..3f142d4e3a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/DatePicker/BitDatePickerDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/DatePicker/BitDatePickerDemo.razor.cs @@ -329,13 +329,6 @@ public partial class BitDatePickerDemo Description = "Whether the date-picker is rendered standalone or with the input component and callout.", }, new() - { - Name = "TabIndex", - Type = "int", - DefaultValue = "0", - Description = "The tabIndex of the DatePicker's input." - }, - new() { Name = "TimeFormat", Type = "BitTimeFormat", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/DateRangePicker/BitDateRangePickerDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/DateRangePicker/BitDateRangePickerDemo.razor.cs index 8ac996f6a9..d63b90726f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/DateRangePicker/BitDateRangePickerDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/DateRangePicker/BitDateRangePickerDemo.razor.cs @@ -357,13 +357,6 @@ public partial class BitDateRangePickerDemo LinkType = LinkType.Link }, new() - { - Name = "TabIndex", - Type = "int", - DefaultValue = "0", - Description = "The tabIndex of the DateRangePicker's input.", - }, - new() { Name = "TimeFormat", Type = "BitTimeFormat", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs index 4e11458226..1df5172335 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs @@ -247,13 +247,6 @@ public partial class BitTextFieldDemo Description = "Shows the custom suffix for text field.", }, new() - { - Name = "TabIndex", - Type = "string?", - DefaultValue = "null", - Description = "The value of the tabindex html attribute of the input element.", - }, - new() { Name = "Trim", Type = "bool", diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TimePicker/BitTimePickerDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TimePicker/BitTimePickerDemo.razor.cs index 887f48e877..95a172e157 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TimePicker/BitTimePickerDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TimePicker/BitTimePickerDemo.razor.cs @@ -190,13 +190,6 @@ public partial class BitTimePickerDemo Description = "Whether the BitTimePicker is rendered standalone or with the input component and callout.", }, new() - { - Name = "TabIndex", - Type = "int", - DefaultValue = "0", - Description = "The tabIndex of the TextField.", - }, - new() { Name = "TimeFormat", Type = "BitTimeFormat", From d62044e70b535dbfa269486a9e26e71d4ebeb804 Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Sun, 30 Nov 2025 17:51:00 +0330 Subject: [PATCH 31/39] feat(besql): configure bit Besql dbContext so it can resolve scoped services #11775 (#11776) --- ...extFactory.cs => BesqlDbContextFactory.cs} | 13 +-- .../Bit.Besql/BesqlDbContextInterceptor.cs | 2 +- src/Besql/Bit.Besql/Bit.Besql.csproj | 1 + ...FactoryBase.cs => DbContextFactoryBase.cs} | 23 +++-- .../IServiceCollectionBesqlExtentions.cs | 16 ++-- src/Besql/Bit.Besql/wwwroot/bit-besql.js | 6 +- .../OfflineDbContextModelBuilder.cs | 4 +- ...0240105010046_InitialMigration.Designer.cs | 70 --------------- .../20251130123559_Initial.Designer.cs | 69 ++++++++++++++ ...Migration.cs => 20251130123559_Initial.cs} | 2 +- .../OfflineDbContextModelSnapshot.cs | 89 +++++++++---------- .../Extensions/ServiceCollectionExtensions.cs | 3 +- .../Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj | 4 - 13 files changed, 152 insertions(+), 150 deletions(-) rename src/Besql/Bit.Besql/{BesqlPooledDbContextFactory.cs => BesqlDbContextFactory.cs} (83%) rename src/Besql/Bit.Besql/{PooledDbContextFactoryBase.cs => DbContextFactoryBase.cs} (65%) delete mode 100644 src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20240105010046_InitialMigration.Designer.cs create mode 100644 src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20251130123559_Initial.Designer.cs rename src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/{20240105010046_InitialMigration.cs => 20251130123559_Initial.cs} (97%) diff --git a/src/Besql/Bit.Besql/BesqlPooledDbContextFactory.cs b/src/Besql/Bit.Besql/BesqlDbContextFactory.cs similarity index 83% rename from src/Besql/Bit.Besql/BesqlPooledDbContextFactory.cs rename to src/Besql/Bit.Besql/BesqlDbContextFactory.cs index 66fdc17172..4e6198c6ee 100644 --- a/src/Besql/Bit.Besql/BesqlPooledDbContextFactory.cs +++ b/src/Besql/Bit.Besql/BesqlDbContextFactory.cs @@ -1,13 +1,14 @@ using System.Data.Common; using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Components.WebAssembly.Services; namespace Bit.Besql; -public class BesqlPooledDbContextFactory : PooledDbContextFactoryBase +public class BesqlDbContextFactory : DbContextFactoryBase where TDbContext : DbContext { private readonly string _fileName; @@ -15,12 +16,12 @@ public class BesqlPooledDbContextFactory : PooledDbContextFactoryBas private readonly IBitBesqlStorage _storage; private readonly IServiceProvider _serviceProvider; - public BesqlPooledDbContextFactory( - IBitBesqlStorage storage, + public BesqlDbContextFactory( + IServiceProvider serviceProvider, DbContextOptions options, + IDbContextFactorySource factorySource, Func dbContextInitializer, - IServiceProvider serviceProvider) - : base(options, dbContextInitializer) + IBitBesqlStorage storage) : base(serviceProvider, options, factorySource, dbContextInitializer) { _connectionString = options.Extensions .OfType() diff --git a/src/Besql/Bit.Besql/BesqlDbContextInterceptor.cs b/src/Besql/Bit.Besql/BesqlDbContextInterceptor.cs index c3b6889da5..7ac9376593 100644 --- a/src/Besql/Bit.Besql/BesqlDbContextInterceptor.cs +++ b/src/Besql/Bit.Besql/BesqlDbContextInterceptor.cs @@ -7,7 +7,7 @@ namespace Bit.Besql; public class BesqlDbContextInterceptor(IBitBesqlStorage storage) : IDbCommandInterceptor, ISingletonInterceptor { - private readonly string[] keywords = ["INSERT", "UPDATE", "DELETE", "CREATE", "ALTER", "DROP"]; + private readonly string[] keywords = ["INSERT INTO ", "UPDATE ", "DELETE FROM ", "CREATE ", "ALTER ", "DROP "]; public DbDataReader ReaderExecuted( DbCommand command, diff --git a/src/Besql/Bit.Besql/Bit.Besql.csproj b/src/Besql/Bit.Besql/Bit.Besql.csproj index e7ebe5104f..630bc3e186 100644 --- a/src/Besql/Bit.Besql/Bit.Besql.csproj +++ b/src/Besql/Bit.Besql/Bit.Besql.csproj @@ -5,6 +5,7 @@ net10.0;net9.0;net8.0 enable + $(NoWarn);IL2091;EF1001 enable diff --git a/src/Besql/Bit.Besql/PooledDbContextFactoryBase.cs b/src/Besql/Bit.Besql/DbContextFactoryBase.cs similarity index 65% rename from src/Besql/Bit.Besql/PooledDbContextFactoryBase.cs rename to src/Besql/Bit.Besql/DbContextFactoryBase.cs index aecd6d900f..2bd3831c59 100644 --- a/src/Besql/Bit.Besql/PooledDbContextFactoryBase.cs +++ b/src/Besql/Bit.Besql/DbContextFactoryBase.cs @@ -1,17 +1,20 @@ -using System.Diagnostics.CodeAnalysis; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Infrastructure; namespace Bit.Besql; -public class PooledDbContextFactoryBase(DbContextOptions options, - Func dbContextInitializer) : PooledDbContextFactory(options) +public class DbContextFactoryBase(IServiceProvider serviceProvider, + DbContextOptions options, + IDbContextFactorySource factorySource, + Func dbContextInitializer) : IDbContextFactory where TDbContext : DbContext { private TaskCompletionSource? dbContextInitializerTcs; [RequiresUnreferencedCode("Calls StartRunningDbContextInitializer()")] - public override async Task CreateDbContextAsync(CancellationToken cancellationToken = default) + public virtual async Task CreateDbContextAsync(CancellationToken cancellationToken = default) { if (dbContextInitializerTcs is null) { @@ -20,12 +23,14 @@ public override async Task CreateDbContextAsync(CancellationToken ca await dbContextInitializerTcs!.Task.ConfigureAwait(false); - return await base.CreateDbContextAsync(cancellationToken).ConfigureAwait(false); + var dbContext = factorySource.Factory(serviceProvider, options); + + return dbContext; } - public override TDbContext CreateDbContext() + public virtual TDbContext CreateDbContext() { - return CreateDbContextAsync().GetAwaiter().GetResult(); + return factorySource.Factory(serviceProvider, options); } [RequiresUnreferencedCode("Calls Bit.Besql.PooledDbContextFactoryBase.InitializeDbContext()")] @@ -52,7 +57,7 @@ protected virtual async Task InitializeDbContext() { if (dbContextInitializer is not null) { - await using var dbContext = await base.CreateDbContextAsync().ConfigureAwait(false); + await using var dbContext = factorySource.Factory(serviceProvider, options); await dbContextInitializer(dbContext.GetService(), dbContext).ConfigureAwait(false); } } diff --git a/src/Besql/Bit.Besql/IServiceCollectionBesqlExtentions.cs b/src/Besql/Bit.Besql/IServiceCollectionBesqlExtentions.cs index b789a34d32..694809f51c 100644 --- a/src/Besql/Bit.Besql/IServiceCollectionBesqlExtentions.cs +++ b/src/Besql/Bit.Besql/IServiceCollectionBesqlExtentions.cs @@ -16,7 +16,8 @@ public static class IServiceCollectionBesqlExtentions #endif public static IServiceCollection AddBesqlDbContextFactory(this IServiceCollection services, Action? optionsAction = null, - Func? dbContextInitializer = null) + Func? dbContextInitializer = null, + ServiceLifetime lifetime = ServiceLifetime.Singleton) where TDbContext : DbContext { optionsAction ??= (_, _) => { }; @@ -37,25 +38,25 @@ public static IServiceCollection AddBesqlDbContextFactory(this IServ services.TryAddSingleton(); // To make optimized db context work in blazor wasm: https://github.com/dotnet/efcore/issues/31751 // https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-di%2Cexpression-api-with-constant#compiled-models - services.AddDbContextFactory>((serviceProvider, options) => + services.AddDbContextFactory>((serviceProvider, options) => { options.AddInterceptors(serviceProvider.GetRequiredService()); #if NET9_0_OR_GREATER options.ReplaceService(); #endif optionsAction.Invoke(serviceProvider, options); - }); + }, lifetime: lifetime); } else { services.TryAddSingleton(); - services.AddDbContextFactory>((serviceProvider, options) => + services.AddDbContextFactory>((serviceProvider, options) => { #if NET9_0_OR_GREATER options.ReplaceService(); #endif optionsAction.Invoke(serviceProvider, options); - }); + }, lifetime: lifetime); } return services; @@ -63,12 +64,13 @@ public static IServiceCollection AddBesqlDbContextFactory(this IServ public static IServiceCollection AddBesqlDbContextFactory(this IServiceCollection services, Action? optionsAction = null, - Func? dbContextInitializer = null) + Func? dbContextInitializer = null, + ServiceLifetime lifetime = ServiceLifetime.Singleton) where TDbContext : DbContext { optionsAction ??= _ => { }; dbContextInitializer ??= async _ => { }; - return services.AddBesqlDbContextFactory((serviceProvider, options) => optionsAction.Invoke(options), (serviceProvider, dbContext) => dbContextInitializer.Invoke(dbContext)); + return services.AddBesqlDbContextFactory((serviceProvider, options) => optionsAction.Invoke(options), (serviceProvider, dbContext) => dbContextInitializer.Invoke(dbContext), lifetime); } } diff --git a/src/Besql/Bit.Besql/wwwroot/bit-besql.js b/src/Besql/Bit.Besql/wwwroot/bit-besql.js index e65e81c7e6..9962486065 100644 --- a/src/Besql/Bit.Besql/wwwroot/bit-besql.js +++ b/src/Besql/Bit.Besql/wwwroot/bit-besql.js @@ -1,7 +1,7 @@ -var BitBesql = window.BitBesql || {}; +var BitBesql = window.BitBesql || {}; BitBesql.version = window['bit-besql version'] = '10.1.1'; -BitBesql.persist = async function persist(fileName) { +BitBesql.persist = async function besqlPersist(fileName) { if (BitBesql.dbCache == null) { BitBesql.dbCache = await caches.open('bit-Besql'); @@ -33,7 +33,7 @@ BitBesql.persist = async function persist(fileName) { await dbCache.put(cacheStorageFilePath, response); } -BitBesql.load = async function init(fileName) { +BitBesql.load = async function besqlLoad(fileName) { const sqliteFilePath = `/${fileName}`; const cacheStorageFilePath = `/data/cache/${fileName}`; diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModelBuilder.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModelBuilder.cs index 8c7d4b8682..b6f3265753 100644 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModelBuilder.cs +++ b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/CompiledModel/OfflineDbContextModelBuilder.cs @@ -11,7 +11,7 @@ namespace Bit.Besql.Demo.Client.Data.CompiledModel public partial class OfflineDbContextModel { private OfflineDbContextModel() - : base(skipDetectChanges: false, modelId: new Guid("ac96847b-e3a9-46a3-82cf-7605a37f26af"), entityTypeCount: 1) + : base(skipDetectChanges: false, modelId: new Guid("a517c835-3ee2-49c8-b15c-99478d0fbb8b"), entityTypeCount: 1) { } @@ -21,7 +21,7 @@ partial void Initialize() WeatherForecastEntityType.CreateAnnotations(weatherForecast); - AddAnnotation("ProductVersion", "9.0.0"); + AddAnnotation("ProductVersion", "10.0.0"); } } } diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20240105010046_InitialMigration.Designer.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20240105010046_InitialMigration.Designer.cs deleted file mode 100644 index 49f4f3dc13..0000000000 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20240105010046_InitialMigration.Designer.cs +++ /dev/null @@ -1,70 +0,0 @@ -// -using Bit.Besql.Demo.Client.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Bit.Besql.Demo.Client.Data.Migrations -{ - [DbContext(typeof(OfflineDbContext))] - [Migration("20240105010046_InitialMigration")] - partial class InitialMigration - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); - - modelBuilder.Entity("Bit.Besql.Demo.Client.Model.WeatherForecast", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Date") - .HasColumnType("INTEGER"); - - b.Property("Summary") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("TemperatureC") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("TemperatureC"); - - b.ToTable("WeatherForecasts"); - - b.HasData( - new - { - Id = 1, - Date = 1307437068492800000L, - Summary = "Hot", - TemperatureC = 30 - }, - new - { - Id = 2, - Date = 1307438837964800000L, - Summary = "Normal", - TemperatureC = 20 - }, - new - { - Id = 3, - Date = 1307440607436800000L, - Summary = "Cold", - TemperatureC = 10 - }); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20251130123559_Initial.Designer.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20251130123559_Initial.Designer.cs new file mode 100644 index 0000000000..c191090f2c --- /dev/null +++ b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20251130123559_Initial.Designer.cs @@ -0,0 +1,69 @@ +// +using Bit.Besql.Demo.Client.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.Besql.Demo.Client.Data.Migrations; + +[DbContext(typeof(OfflineDbContext))] +[Migration("20251130123559_Initial")] +partial class Initial +{ + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.0"); + + modelBuilder.Entity("Bit.Besql.Demo.Client.Model.WeatherForecast", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .HasColumnType("INTEGER"); + + b.Property("Summary") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("TemperatureC") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TemperatureC"); + + b.ToTable("WeatherForecasts"); + + b.HasData( + new + { + Id = 1, + Date = 1307437068492800000L, + Summary = "Hot", + TemperatureC = 30 + }, + new + { + Id = 2, + Date = 1307438837964800000L, + Summary = "Normal", + TemperatureC = 20 + }, + new + { + Id = 3, + Date = 1307440607436800000L, + Summary = "Cold", + TemperatureC = 10 + }); + }); +#pragma warning restore 612, 618 + } +} diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20240105010046_InitialMigration.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20251130123559_Initial.cs similarity index 97% rename from src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20240105010046_InitialMigration.cs rename to src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20251130123559_Initial.cs index c5a0b27b6c..27f074ece4 100644 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20240105010046_InitialMigration.cs +++ b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/20251130123559_Initial.cs @@ -7,7 +7,7 @@ namespace Bit.Besql.Demo.Client.Data.Migrations; /// -public partial class InitialMigration : Migration +public partial class Initial : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/OfflineDbContextModelSnapshot.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/OfflineDbContextModelSnapshot.cs index 620bdb2a96..10b33fb37a 100644 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/OfflineDbContextModelSnapshot.cs +++ b/src/Besql/Demo/Bit.Besql.Demo.Client/Data/Migrations/OfflineDbContextModelSnapshot.cs @@ -6,62 +6,61 @@ #nullable disable -namespace Bit.Besql.Demo.Client.Data.Migrations +namespace Bit.Besql.Demo.Client.Data.Migrations; + +[DbContext(typeof(OfflineDbContext))] +partial class OfflineDbContextModelSnapshot : ModelSnapshot { - [DbContext(typeof(OfflineDbContext))] - partial class OfflineDbContextModelSnapshot : ModelSnapshot + protected override void BuildModel(ModelBuilder modelBuilder) { - protected override void BuildModel(ModelBuilder modelBuilder) - { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + modelBuilder.HasAnnotation("ProductVersion", "10.0.0"); - modelBuilder.Entity("Bit.Besql.Demo.Client.Model.WeatherForecast", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + modelBuilder.Entity("Bit.Besql.Demo.Client.Model.WeatherForecast", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("Date") - .HasColumnType("INTEGER"); + b.Property("Date") + .HasColumnType("INTEGER"); - b.Property("Summary") - .HasMaxLength(100) - .HasColumnType("TEXT"); + b.Property("Summary") + .HasMaxLength(100) + .HasColumnType("TEXT"); - b.Property("TemperatureC") - .HasColumnType("INTEGER"); + b.Property("TemperatureC") + .HasColumnType("INTEGER"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("TemperatureC"); + b.HasIndex("TemperatureC"); - b.ToTable("WeatherForecasts"); + b.ToTable("WeatherForecasts"); - b.HasData( - new - { - Id = 1, - Date = 1307437068492800000L, - Summary = "Hot", - TemperatureC = 30 - }, - new - { - Id = 2, - Date = 1307438837964800000L, - Summary = "Normal", - TemperatureC = 20 - }, - new - { - Id = 3, - Date = 1307440607436800000L, - Summary = "Cold", - TemperatureC = 10 - }); - }); + b.HasData( + new + { + Id = 1, + Date = 1307437068492800000L, + Summary = "Hot", + TemperatureC = 30 + }, + new + { + Id = 2, + Date = 1307438837964800000L, + Summary = "Normal", + TemperatureC = 20 + }, + new + { + Id = 3, + Date = 1307440607436800000L, + Summary = "Cold", + TemperatureC = 10 + }); + }); #pragma warning restore 612, 618 - } } } diff --git a/src/Besql/Demo/Bit.Besql.Demo.Client/Extensions/ServiceCollectionExtensions.cs b/src/Besql/Demo/Bit.Besql.Demo.Client/Extensions/ServiceCollectionExtensions.cs index 1022b99c5c..7a608962eb 100644 --- a/src/Besql/Demo/Bit.Besql.Demo.Client/Extensions/ServiceCollectionExtensions.cs +++ b/src/Besql/Demo/Bit.Besql.Demo.Client/Extensions/ServiceCollectionExtensions.cs @@ -1,6 +1,5 @@ using Bit.Besql.Demo.Client.Data; using Microsoft.EntityFrameworkCore; -using Bit.Besql.Demo.Client.Data.CompiledModel; namespace Microsoft.Extensions.DependencyInjection; @@ -11,7 +10,7 @@ public static IServiceCollection AddAppServices(this IServiceCollection services services.AddBesqlDbContextFactory((sp, optionsBuilder) => { optionsBuilder - .UseModel(OfflineDbContextModel.Instance) // use generated compiled model in order to make db context optimized + .UseModel(Bit.Besql.Demo.Client.Data.CompiledModel.OfflineDbContextModel.Instance) // use generated compiled model in order to make db context optimized .UseSqlite($"Data Source=Offline-Client.db"); }, dbContextInitializer: async (sp, dbContext) => await dbContext.Database.MigrateAsync()); diff --git a/src/Besql/Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj b/src/Besql/Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj index 9b8e914360..9ec5d930bf 100644 --- a/src/Besql/Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj +++ b/src/Besql/Demo/Bit.Besql.Demo/Bit.Besql.Demo.csproj @@ -17,10 +17,6 @@ and open Nuget Package Manager Console, and select `Bit.Besql.Demo.Client` project as default project Then run either Add-Migration MigrationName -OutputDir Data\Migrations or Optimize-DbContext -OutputDir Data\CompiledModel commands. --> - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive From 832e4966156196ceb3dc918acd059f98aab0396a Mon Sep 17 00:00:00 2001 From: Yaser Moradi Date: Sun, 30 Nov 2025 18:10:44 +0330 Subject: [PATCH 32/39] feat(prerelease): v-10.2.0-pre-01 #11777 (#11778) --- src/Besql/Bit.Besql/wwwroot/bit-besql.js | 2 +- src/Bit.Build.props | 2 +- .../buildTransitive/Bit.BlazorES2019.targets | 8 ++++---- src/BlazorUI/Bit.BlazorUI/Info.cs | 2 +- src/BlazorUI/Bit.BlazorUI/Scripts/general.ts | 2 +- .../Bit.BlazorUI.Demo.Server.csproj | 6 +++--- .../Bit.BlazorUI.Demo.Shared.csproj | 4 ++-- .../Bit.BlazorUI.Demo.Client.Core.csproj | 4 ++-- .../Bit.BlazorUI.Demo.Client.Maui.csproj | 6 +++--- .../Bit.BlazorUI.Demo.Client.Web.csproj | 6 +++--- .../wwwroot/service-worker.published.js | 2 +- .../Bit.BlazorUI.Demo.Client.Windows.csproj | 6 +++--- src/BlazorUI/Demo/Directory.Build.props | 2 +- .../Bit.Bswup.Demo/wwwroot/service-worker.js | 2 +- .../wwwroot/service-worker.published.js | 2 +- .../wwwroot/service-worker.js | 2 +- .../wwwroot/service-worker.published.js | 2 +- .../Bit.Bswup/Scripts/bit-bswup.progress.ts | 2 +- .../Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts | 2 +- src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts | 2 +- src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts | 2 +- .../FullDemo/Client/wwwroot/service-worker.js | 2 +- .../wwwroot/service-worker.published.js | 2 +- src/Butil/Bit.Butil/Scripts/butil.ts | 2 +- .../BlazorEmpty.Client.csproj | 8 ++++---- .../BlazorEmpty/BlazorEmpty.csproj | 8 ++++---- .../.docs/19- Project Miscellaneous Files.md | 10 +++++----- .../Boilerplate/Bit.Boilerplate/README.md | 2 +- .../wwwroot/service-worker.published.js | 2 +- .../src/Directory.Packages.props | 20 +++++++++---------- .../Bit.Websites.Careers.Client.csproj | 12 +++++------ .../wwwroot/service-worker.published.js | 2 +- .../Bit.Websites.Careers.Server.csproj | 6 +++--- .../Bit.Websites.Careers.Shared.csproj | 4 ++-- .../Careers/src/Directory.Build.props | 2 +- .../Bit.Websites.Platform.Client.csproj | 14 ++++++------- .../Templates03GettingStartedPage.razor | 2 +- .../Templates03GettingStartedPage.razor.cs | 2 +- .../wwwroot/service-worker.published.js | 2 +- .../Bit.Websites.Platform.Server.csproj | 6 +++--- .../Bit.Websites.Platform.Shared.csproj | 4 ++-- .../Platform/src/Directory.Build.props | 2 +- .../Bit.Websites.Sales.Client.csproj | 12 +++++------ .../wwwroot/service-worker.published.js | 2 +- .../Bit.Websites.Sales.Server.csproj | 6 +++--- .../Bit.Websites.Sales.Shared.csproj | 4 ++-- src/Websites/Sales/src/Directory.Build.props | 2 +- 47 files changed, 104 insertions(+), 104 deletions(-) diff --git a/src/Besql/Bit.Besql/wwwroot/bit-besql.js b/src/Besql/Bit.Besql/wwwroot/bit-besql.js index 9962486065..d556d8bcc3 100644 --- a/src/Besql/Bit.Besql/wwwroot/bit-besql.js +++ b/src/Besql/Bit.Besql/wwwroot/bit-besql.js @@ -1,5 +1,5 @@ var BitBesql = window.BitBesql || {}; -BitBesql.version = window['bit-besql version'] = '10.1.1'; +BitBesql.version = window['bit-besql version'] = '10.2.0-pre-01'; BitBesql.persist = async function besqlPersist(fileName) { diff --git a/src/Bit.Build.props b/src/Bit.Build.props index 625003cbb7..5c01347268 100644 --- a/src/Bit.Build.props +++ b/src/Bit.Build.props @@ -27,7 +27,7 @@ https://github.com/bitfoundation/bitplatform - 10.1.1 + 10.2.0-pre-01 $(ReleaseVersion) https://github.com/bitfoundation/bitplatform/releases/tag/v-$(ReleaseVersion) $([System.String]::Copy($(ReleaseVersion)).Replace('-pre-', '.')) diff --git a/src/BlazorES2019/Bit.BlazorES2019/buildTransitive/Bit.BlazorES2019.targets b/src/BlazorES2019/Bit.BlazorES2019/buildTransitive/Bit.BlazorES2019.targets index 77598fa19f..21f7652a4e 100644 --- a/src/BlazorES2019/Bit.BlazorES2019/buildTransitive/Bit.BlazorES2019.targets +++ b/src/BlazorES2019/Bit.BlazorES2019/buildTransitive/Bit.BlazorES2019.targets @@ -1,13 +1,13 @@ - - + + PreserveNewest PreserveNewest wwwroot\_framework\%(Filename)%(Extension) - - + + PreserveNewest PreserveNewest wwwroot\_framework\%(Filename)%(Extension) diff --git a/src/BlazorUI/Bit.BlazorUI/Info.cs b/src/BlazorUI/Bit.BlazorUI/Info.cs index 366b67ffcb..e47a799a03 100644 --- a/src/BlazorUI/Bit.BlazorUI/Info.cs +++ b/src/BlazorUI/Bit.BlazorUI/Info.cs @@ -8,5 +8,5 @@ public static class Info /// /// The current version string of bit BlazorUI. /// - public static string Version { get; } = "10.1.1"; + public static string Version { get; } = "10.2.0-pre-01"; } diff --git a/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts b/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts index faf2f90b0d..6402ff253f 100644 --- a/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts +++ b/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts @@ -1,4 +1,4 @@ -(BitBlazorUI as any).version = (window as any)['bit-blazorui version'] = '10.1.1'; +(BitBlazorUI as any).version = (window as any)['bit-blazorui version'] = '10.2.0-pre-01'; interface DotNetObject { invokeMethod(methodIdentifier: string, ...args: any[]): T; diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj index 2f6e995406..df6e20daa6 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj @@ -10,12 +10,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj index 06be4c81ed..1af060067e 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj @@ -5,11 +5,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj index a8a9aac4ea..aef353fe70 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj @@ -16,11 +16,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj index fb3a60bb5c..68525b885f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj @@ -98,13 +98,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj index ecf33b68f3..d047c0a80c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj @@ -28,13 +28,13 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js index 80f5853e5b..6142902876 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 // https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup self.assetsInclude = []; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj index e4657f7643..93e750aa3a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj @@ -29,12 +29,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Directory.Build.props b/src/BlazorUI/Demo/Directory.Build.props index 9b98a3ab53..8580f3f882 100644 --- a/src/BlazorUI/Demo/Directory.Build.props +++ b/src/BlazorUI/Demo/Directory.Build.props @@ -1,4 +1,4 @@ - + 14.0 diff --git a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js index 428402e0a8..893ec57ac4 100644 --- a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js +++ b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; self.caseInsensitiveUrl = true; diff --git a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js index 8acdbcc7dc..b5fc469071 100644 --- a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js +++ b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; self.caseInsensitiveUrl = true; diff --git a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js index bb529a954f..00ff497a41 100644 --- a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js +++ b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 // In development, always fetch from the network and do not enable offline support. // This is because caching would make development more difficult (changes would not diff --git a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js index b501eaa051..e3b71ddfbd 100644 --- a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js +++ b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 self.assetsInclude = []; self.assetsExclude = [ diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts index 3343436b18..6ca1bd2227 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts @@ -1,4 +1,4 @@ -window['bit-bswup.progress version'] = '10.1.1'; +window['bit-bswup.progress version'] = '10.2.0-pre-01'; (function () { const _config: IBswupProgressConfigs = {}; diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts index e0617ccedc..637679d535 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts @@ -1,4 +1,4 @@ -self['bit-bswup.sw-cleanup version'] = '10.1.1'; +self['bit-bswup.sw-cleanup version'] = '10.2.0-pre-01'; self.addEventListener('install', e => e.waitUntil(removeBswup())); diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts index f2879b59f6..b8c948662d 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts @@ -1,4 +1,4 @@ -self['bit-bswup.sw version'] = '10.1.1'; +self['bit-bswup.sw version'] = '10.2.0-pre-01'; interface Window { clients: any diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts index 61645b6725..9f137dabd1 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts @@ -1,5 +1,5 @@ var BitBswup = BitBswup || {}; -BitBswup.version = window['bit-bswup version'] = '10.1.1'; +BitBswup.version = window['bit-bswup version'] = '10.2.0-pre-01'; (function () { const bitBswupScript = document.currentScript; diff --git a/src/Bswup/FullDemo/Client/wwwroot/service-worker.js b/src/Bswup/FullDemo/Client/wwwroot/service-worker.js index 21ee6ab3f9..fbb6ce9d46 100644 --- a/src/Bswup/FullDemo/Client/wwwroot/service-worker.js +++ b/src/Bswup/FullDemo/Client/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 // In development, always fetch from the network and do not enable offline support. // This is because caching would make development more difficult (changes would not diff --git a/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js b/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js index 63f953f1fd..5f42270b6f 100644 --- a/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js +++ b/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 self.assetsInclude = []; self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; diff --git a/src/Butil/Bit.Butil/Scripts/butil.ts b/src/Butil/Bit.Butil/Scripts/butil.ts index 1a58bea165..8568c1c6a2 100644 --- a/src/Butil/Bit.Butil/Scripts/butil.ts +++ b/src/Butil/Bit.Butil/Scripts/butil.ts @@ -1,2 +1,2 @@ var BitButil = BitButil || {}; -BitButil.version = window['bit-butil version'] = '10.1.1'; \ No newline at end of file +BitButil.version = window['bit-butil version'] = '10.2.0-pre-01'; \ No newline at end of file diff --git a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj index 7fc2e7c329..7e8f812d5e 100644 --- a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj +++ b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj @@ -1,4 +1,4 @@ - + @@ -15,14 +15,14 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj index 775ce4ed7d..3f586209da 100644 --- a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj +++ b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj @@ -1,4 +1,4 @@ - + @@ -18,14 +18,14 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/19- Project Miscellaneous Files.md b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/19- Project Miscellaneous Files.md index 5da9b22f75..be7850ad2f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.docs/19- Project Miscellaneous Files.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.docs/19- Project Miscellaneous Files.md @@ -222,7 +222,7 @@ These namespaces are **automatically imported** in every C# file, eliminating th Instead of this in each `.csproj`: ```xml - + ``` You write this in `.csproj`: @@ -232,7 +232,7 @@ You write this in `.csproj`: And the version is defined centrally in `Directory.Packages.props`: ```xml - + ``` **Benefits**: @@ -243,8 +243,8 @@ And the version is defined centrally in `Directory.Packages.props`: **Example packages included**: ```xml - - + + @@ -599,7 +599,7 @@ When you open the project in VS Code, you'll be prompted to install these extens **Example**: ````markdown -This project gets generated by bit-bp template v-10.1.1 using the following command +This project gets generated by bit-bp template v-10.2.0-pre-01 using the following command ```bash dotnet new bit-bp --name Boilerplate diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/README.md b/src/Templates/Boilerplate/Bit.Boilerplate/README.md index 5dc4babd97..a351c46719 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/README.md +++ b/src/Templates/Boilerplate/Bit.Boilerplate/README.md @@ -2,7 +2,7 @@ Thank you for creating a new project with bit platform! We appreciate your trust in our platform and are excited to see what you'll build. -This project gets generated by bit-bp template v-10.1.1 using the following command +This project gets generated by bit-bp template v-10.2.0-pre-01 using the following command ```bash dotnet new bit-bp --name Boilerplate diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js index f1b2226af8..58ec6afe6d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js @@ -1,5 +1,5 @@ //+:cnd:noEmit -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 // https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup //#if (notification == true) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props index 22428bff1c..6d6f8796c3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props @@ -1,15 +1,15 @@ - - - - - - - - - + + + + + + + + + @@ -82,7 +82,7 @@ - + diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj index 5f56684efd..c3c15f2b96 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj @@ -29,15 +29,15 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/wwwroot/service-worker.published.js b/src/Websites/Careers/src/Bit.Websites.Careers.Client/wwwroot/service-worker.published.js index a13925991b..8208f84770 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/wwwroot/service-worker.published.js +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 // https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup self.assetsInclude = []; diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj index fdfa61430a..26b4356e33 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj @@ -11,12 +11,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj index 0438c7690c..7040a66d39 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj @@ -6,11 +6,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Careers/src/Directory.Build.props b/src/Websites/Careers/src/Directory.Build.props index 4fa8f1d327..7b5c044927 100644 --- a/src/Websites/Careers/src/Directory.Build.props +++ b/src/Websites/Careers/src/Directory.Build.props @@ -1,4 +1,4 @@ - + 14.0 diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj index 799092b21e..922a01c168 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj @@ -32,16 +32,16 @@ - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor index 764474cb31..3802c47a0c 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor @@ -153,7 +153,7 @@ rm $HOME/dotnet.tar.gz
  • Install Bit Boilerplate project template
    - dotnet new install Bit.Boilerplate::10.1.1 + dotnet new install Bit.Boilerplate::10.2.0-pre-01
  • @if (showCrossPlatform && devOS is "Windows") { diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs index eaca002381..cd458fe62d 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs @@ -43,7 +43,7 @@ public partial class Templates03GettingStartedPage command:"dotnet tool install -g Aspire.Cli"), (text:@"echo 'Install the Bit.Boilerplate project template https://www.nuget.org/packages/Boilerplate.Templates';", - command:"dotnet new install Bit.Boilerplate::10.1.1;") + command:"dotnet new install Bit.Boilerplate::10.2.0-pre-01;") ]; if (enableVirtualization) diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/service-worker.published.js b/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/service-worker.published.js index cd781cfb83..67d003a393 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/service-worker.published.js +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 // https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup self.assetsInclude = []; diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj index ba9cca1914..913ef41ba4 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj @@ -12,12 +12,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj index 0438c7690c..7040a66d39 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj @@ -6,11 +6,11 @@
    - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Platform/src/Directory.Build.props b/src/Websites/Platform/src/Directory.Build.props index b913e2ed52..36b7252ddb 100644 --- a/src/Websites/Platform/src/Directory.Build.props +++ b/src/Websites/Platform/src/Directory.Build.props @@ -1,4 +1,4 @@ - + preview diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj index 15f9c2854d..3a0609f300 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj @@ -29,15 +29,15 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/wwwroot/service-worker.published.js b/src/Websites/Sales/src/Bit.Websites.Sales.Client/wwwroot/service-worker.published.js index 74d7ae4af5..7a1f6207bd 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/wwwroot/service-worker.published.js +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 10.1.1 +// bit version: 10.2.0-pre-01 // https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup self.assetsInclude = []; diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj index 83339a5250..dc757a0e81 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj @@ -11,12 +11,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj index 0438c7690c..7040a66d39 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj @@ -6,11 +6,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Sales/src/Directory.Build.props b/src/Websites/Sales/src/Directory.Build.props index 5d59052268..19512bf855 100644 --- a/src/Websites/Sales/src/Directory.Build.props +++ b/src/Websites/Sales/src/Directory.Build.props @@ -1,4 +1,4 @@ - + 14.0 From ba95b8200f5bed744a854f392c3cd474effb2a1f Mon Sep 17 00:00:00 2001 From: Saleh Yusefnejad Date: Mon, 1 Dec 2025 15:11:56 +0330 Subject: [PATCH 33/39] feat(blazorui): apply BitActionButton improvements #11781 (#11782) --- .../ActionButton/BitActionButton.razor | 2 +- .../ActionButton/BitActionButton.razor.cs | 48 ++- .../Buttons/ActionButton/BitActionButton.scss | 14 +- .../BitActionButtonClassStyles.cs | 9 +- src/BlazorUI/BitActionButtonDemo.restore | Bin 0 -> 29850 bytes .../Components/DemoExample.razor.scss | 3 +- .../ActionButton/BitActionButtonDemo.razor | 317 ++++++++++++------ .../ActionButton/BitActionButtonDemo.razor.cs | 44 ++- .../BitActionButtonDemo.razor.samples.cs | 295 +++++++++++----- src/BlazorUI/temp_restore_razor.txt | 0 10 files changed, 490 insertions(+), 242 deletions(-) create mode 100644 src/BlazorUI/BitActionButtonDemo.restore create mode 100644 src/BlazorUI/temp_restore_razor.txt diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.razor b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.razor index eea77513c4..edf7f9b814 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.razor +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.razor @@ -45,7 +45,7 @@ else aria-describedby="@AriaDescription"> @if (IconName is not null) { - + } @if (ChildContent is not null && IconOnly is false) { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.razor.cs index 06fa449851..901da33eb7 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.razor.cs @@ -3,55 +3,56 @@ namespace Bit.BlazorUI; /// -/// ActionButton is a special type of button with a specific set of visual styles and properties. +/// A lightweight and special type of button/link with icon-first styling, sized presets, and colorized text/icon support. /// public partial class BitActionButton : BitComponentBase { private string? _rel; - private int? _tabIndex; + private string? _tabIndex; private BitButtonType _buttonType; /// - /// The EditContext, which is set if the button is inside an + /// The EditContext, which is set if the button is inside an . + /// The value is coming from the cascading value provided by the EditForm. /// [CascadingParameter] public EditContext? EditContext { get; set; } /// - /// Whether the button can have focus in disabled mode. + /// Keeps the disabled action button focusable by not forcing a negative tabindex when is false. /// [Parameter] public bool AllowDisabledFocus { get; set; } /// - /// Detailed description of the button for the benefit of screen readers. + /// Detailed description of the button for the benefit of screen readers (rendered into aria-describedby). /// [Parameter] public string? AriaDescription { get; set; } /// - /// If true, add an aria-hidden attribute instructing screen readers to ignore the button. + /// If true, adds an aria-hidden attribute instructing screen readers to ignore the button. /// [Parameter] public bool AriaHidden { get; set; } /// - /// The type html attribute of the button element. + /// The type of the button element; defaults to submit inside an otherwise button. /// [Parameter] public BitButtonType? ButtonType { get; set; } /// - /// The content of the button. + /// The custom body of the action button (text and/or any render fragment). /// [Parameter] public RenderFragment? ChildContent { get; set; } /// - /// Custom CSS classes for different parts of the button. + /// Custom CSS classes for the root, icon, and content sections of the action button. /// [Parameter] public BitActionButtonClassStyles? Classes { get; set; } /// - /// The general color of the button. + /// The general color of the button that applies to the icon and text of the action button. /// [Parameter, ResetClassBuilder] public BitColor? Color { get; set; } @@ -71,40 +72,42 @@ public partial class BitActionButton : BitComponentBase public string? Href { get; set; } /// - /// The icon name of the icon to render inside the button. + /// The Fluent UI icon name to render inside the action button (e.g., BitIconName.AddFriend). + /// Browse available names in BitIconName of the Bit.BlazorUI.Icons nuget package or the gallery: . /// [Parameter] public string? IconName { get; set; } /// - /// Removes the container of the text and only renders the icon. + /// Removes the text container and renders only the icon. /// [Parameter] public bool IconOnly { get; set; } /// - /// The callback for the click event of the button. + /// Raised when the action button is clicked (only when is true); receives . /// [Parameter] public EventCallback OnClick { get; set; } /// - /// Custom CSS styles for different parts of the button. + /// Custom inline styles for the root, icon, and content sections of the action button. /// [Parameter] public BitActionButtonClassStyles? Styles { get; set; } /// - /// Reverses the positions of the icon and the content of the button. + /// Swaps the order of the icon and content so the icon appears after the text. /// [Parameter, ResetClassBuilder] public bool ReversedIcon { get; set; } /// - /// If Href provided, specifies the relationship between the current document and the linked document. + /// Sets the rel attribute for link-rendered buttons when is a non-anchor URL; ignored for empty or hash-only hrefs. + /// The rel attribute specifies the relationship between the current document and the linked document. /// [Parameter] [CallOnSet(nameof(OnSetHrefAndRel))] public BitLinkRel? Rel { get; set; } /// - /// The size of the button. + /// Sets the preset size for typography and padding of the action button. /// [Parameter, ResetClassBuilder] public BitSize? Size { get; set; } @@ -120,6 +123,7 @@ public partial class BitActionButton : BitComponentBase [Parameter] public string? Title { get; set; } + protected override string RootElementClass => "bit-acb"; protected override void RegisterCssClasses() @@ -170,7 +174,11 @@ protected override void OnParametersSet() { if (IsEnabled is false) { - _tabIndex = AllowDisabledFocus ? null : -1; + _tabIndex = AllowDisabledFocus ? null : "-1"; + } + else + { + _tabIndex = TabIndex ?? _tabIndex; } _buttonType = ButtonType ?? (EditContext is null ? BitButtonType.Button : BitButtonType.Submit); @@ -178,6 +186,8 @@ protected override void OnParametersSet() base.OnParametersSet(); } + + protected virtual async Task HandleOnClick(MouseEventArgs e) { if (IsEnabled) @@ -186,6 +196,8 @@ protected virtual async Task HandleOnClick(MouseEventArgs e) } } + + private void OnSetHrefAndRel() { if (Rel.HasValue is false || Href.HasNoValue() || Href!.StartsWith('#')) diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.scss b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.scss index aba9eee7f8..ea9f228f8b 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.scss @@ -1,4 +1,4 @@ -@import "../../../Styles/functions.scss"; +@import "../../../Styles/functions.scss"; .bit-acb { border: none; @@ -28,6 +28,12 @@ --bit-acb-clr-ico: var(--bit-acb-clr-active); } + &:focus-visible { + outline: none; + border-radius: $shp-border-radius; + box-shadow: 0 0 0 spacing(0.25) var(--bit-acb-clr-hover); + } + &.bit-dis { cursor: default; color: $clr-fg-dis; @@ -103,12 +109,6 @@ --bit-acb-clr-active: #{$clr-err-active}; } -.bit-acb-err { - --bit-acb-clr-ico: #{$clr-err}; - --bit-acb-clr-hover: #{$clr-err-hover}; - --bit-acb-clr-active: #{$clr-err-active}; -} - .bit-acb-pbg { --bit-acb-clr-ico: #{$clr-bg-pri}; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButtonClassStyles.cs b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButtonClassStyles.cs index 09b5245b51..d8b37e614f 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButtonClassStyles.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButtonClassStyles.cs @@ -1,19 +1,22 @@ namespace Bit.BlazorUI; +/// +/// Defines per-part CSS class/style values for . +/// public class BitActionButtonClassStyles { /// - /// Custom CSS classes/styles for the root element of the BitActionButton. + /// Custom class or style applied to the root element. /// public string? Root { get; set; } /// - /// Custom CSS classes/styles for the Icon of the BitActionButton. + /// Custom class or style applied to the icon element. /// public string? Icon { get; set; } /// - /// Custom CSS classes/styles for the content of the BitActionButton. + /// Custom class or style applied to the content container. /// public string? Content { get; set; } } diff --git a/src/BlazorUI/BitActionButtonDemo.restore b/src/BlazorUI/BitActionButtonDemo.restore new file mode 100644 index 0000000000000000000000000000000000000000..83c5127054ea878799533dbf2cc382ea464ea374 GIT binary patch literal 29850 zcmeHQTTdLx6)xp5QvO5Bk|SGMVC`M+g|HR|UnO5w#?C%QU zH{SunY49rm!T0YSC>jB7&q97wqH%44M&J9q_ZV*<VsQ7+v^e9Q8v_|WBXL`ArJR0PhIN~+Wj3ZmoEAu-Y3A5 zwlKt#MaY!g^=Sh6JREbFkcgpQTYW9Va5sR#mRSe3n}A4KLU4U8o8Ufyd`_ShahR@E z7~V50spl43XRBo^q8ABS#1izJ`W|||Xn8AEkcNZmfBU#72-$7p-&07c4;*Osp`I0kb+qk)X4(sV#RuEp3Emxf?Q8`a6xWlg z6ttfeSkMqXsE5@1dTr*!TVQyKKUy_?i&B5}JgWEXdh~gs*>OlYQXXaUbcK{1!(Il^ zge_|=qR%_{zHj;7q)(5bdB>%7gNN?(Hu^RE4GpuPKuNF1JN_V7JoCyBR163n%1w%3uWItIBYQ8}AB zT0MvE^$>rhk5%`x#;W^4{au02?ZX2d*V+kP%d}c{!_&frPEnFEZx;UQx zh*7sbUqptpbU{t?$Ru<#>emQxa*SWF<0eNY6p8>Z+(B89}p;|&b znGuY@$78b$s+Z7)JY+1yQGdts>Hu*yW1Um`_B%Wo+H7=WJ=@>xSircQ{x9w!l)g7( z$!Qi9>MtxspI21qsg@XrF)In|S_$mgIDOIT&B29SC=W(o+g3V~$A)B1aHIGh^If05-OzX;mEEIg#k&go}4HqeLCvPbyVhvl+P z)PsFr2cIXBqt_DKd})3BC&-6%Eg}m#M0Ufv0lnRsmGM>jy4)C|?1kI*lc|y}%pLd- zR*7=Fi|OHDp-hU^`l(TBX;r z-??hf{|*#MokuDf>0`i`S!2ML31fiNVi+}^AogOkdjP)E7nd<~^;L;x<1xwg@Zx!q zYktkjX_iLjT7#Bq9q+Q*%?S4#QSS~uG0*r5V%kmIUpAj(EO}t{{{VMW<0#kf7NX#H zh^*NUBUsII;tY+gn;ODQXNdgh5R{P40ca{vdvH3ofxE1}a}>P~ zN@)Y3s$0F+kgfXK#WmGWWe7g+x1A}M>)x#S%4|ovM|cqM2*Yatl%jLU(=T`kd7E8`>D&qDc7ouKP~q8SU&Vv{#FX(1>l@|`A~-wZb?{-1RNZZyWe{2 zUO>OIcE$HL%_Be~JkcMazAP=alG==Vq-lI1M|oC_o`S!W0cSU>wOGc6Kf)(+mXdP? zq_cjnz^u*|TF@F6#=Jg3YjQ7LqKgx76+8B+R4S*Mt}7*(m-UA;j;vzI3IS$7LyPAf za~HjQhcSh97Plh6X-*l6>TDe6>R9S#X_rg9%%>|p`xD}ej(*D1u*6@3Cj*ONUGD8j zS>V@oNb#CZ*3JH4jnDqzoj-sV+u+6CIMz#9#q(7l!}I57Ujee88!kWF zuzrA^_yaJh#$?Q_B=(iOYZqQaRuZ~dL%zY8YeroM{_bdDR}y#h*e}ti))3^b~r^b(>tD*qjkg^1-LyPM<@OPDqyVsH}PHp2Sab51)K; zqb-U2&%nDOv^}JcYSTVUv@#%bMP;0rJyY)nUVo9x&#SO?=TRk}OR@2Qh{F|m$)uBO-D?}GZ9rso<9&PKnr_bw#co?a`~ zx%#Z{bwTY-)8lF$GaGHL;?RYZ+tcYP?zx&>eY*?FZ<>Brv+~*KcNK>&q}-lnP z@9NuKP=3?&n^}!iAnP@z+30#0pDv`|p5BM)$hCnmq%Jse)9k@jQ)i?9uKzBi+n&BV zymR&2z1aoD)97>4?nK8$sZ7f1O`gZ0E7`cG{}blhSf`hrWy|CFhdWsJ#xr0zi_Eo| z`)I?nJ$CW`$j%|$#p>uI%nk5VfxexGTsHU6ifh0?b!f7|MPHCTIZ8Jlc#`iWX6t@yH=9 z*1NpTHPe%8am^6B;LuI8m&piYerzTUw=T4zy|F5drd-=egW3hBZkqknBVu1aog$hyyPx%T5@e84y_B$+%)^BkJ+=gju=K= zXn?b^cy*w8ULi(Fu4Tkvb-|S;y9hn!eci9t)(OOVz7!*xwA1px@wAh8$ah-)H^G@@ z9qk@s2OH07=83;eTEbcD!p>grU}l?e|Ma5eO?7$y7inR%x=qXknjx7b$pgzlu7rc-)8yJ zZxeiJ`RqXPi0I)xFj9?}H$Z(qoaleZOnqCii{wtcsXUYyaUZ&zlg9l3x#6wP*kGEPOc`>}PQI zEqpakWaBwu{B^ONvF~2E7y-Gr{9f%u%+D@~Sr}*cM7gGzaTSQ{cPB>t{SltW#?vBr zpQmc@tj;blN7sT{(&5pdwSbL1RPfZRr_GrKc?)1EfR6SzMg|LH0@VqBPCaZX$6 z7+F3)pn94UckVlR$22E(1dYRJvT&7O+V5*fJ()4b$o?W4eFHhl+E@$^r!S#T&X3Z6 zFxE-2gYsFe3&29Gq#ONm@s`tlcY^BzG+tIjb%l*z->V!Fj3_b6txc`2%8zG#K8G}9 zXFvfD-GlYSG$d8q>-y8>*#8#mYhGHerR7Zb;7KN1O4Q}cn%F+%VO-wy9`?Zzyved9 zYvO4m@{5984}1u}M=!jC&yy~N;5(~J w^H|fLO#SipA8voV{crQ}_CN9WFYxQ*?SJ4;@BU}5^qF>`RRaC3O3ASP4~o4fE&u=k literal 0 HcmV?d00001 diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/DemoExample.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/DemoExample.razor.scss index 53e287fecd..21b7edd04a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/DemoExample.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Components/DemoExample.razor.scss @@ -24,6 +24,7 @@ } .header-btn-group { + gap: 2px; width: 100%; display: flex; align-items: center; @@ -134,4 +135,4 @@ pointer-events: none; } } -} +} \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor index 1e9038767b..c5a004aa7a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor @@ -6,28 +6,45 @@ Description="ActionButton component of the bit BlazorUI components" /> - Create account +
    Explore the core features of the BitActionButton to get a feel for its baseline behavior.

    - Reversed Icon + + Create account +
    - Disabled + + Reversed Icon +
    - No Icon + + Disabled + +
    + + AriaLabel + +
    + + No Icon + +
    + +
    -
    Use BitActionButton as a hyperlink to external URLs, opening either in a new tab or the same tab.
    +
    Turn ActionButton into a link and choose whether it opens in the current tab or a new one.

    - Open bitplatform.dev + Open bitplatform.dev in a new tab @@ -37,190 +54,284 @@ -
    Use BitActionButton as a hyperlink to external URLs, with a rel attribute.
    +
    Attach rel attribute values (e.g., nofollow, noreferrer) when using ActionButton as an external link.

    - + Open bitplatform.dev with a rel attribute (nofollow) - + Open bitplatform.dev with a rel attribute (nofollow & noreferrer)
    - -
    Demonstrates BitActionButton with various predefined colors: Primary, Secondary, Tertiary, Info, Success, Warning, SevereWarning, and Error.
    + +
    Drop richer content inside ActionButton with a custom template (text plus spinner in this example).

    +
    + +
    + This is a custom template + +
    +
    +
    +
    + + +
    See submit/reset/button types in action inside a validated form.
    +
    +
    + @if (formIsValidSubmit is false) + { + + + + + +
    + +
    +
    + + Submit + + + Reset + + + Button + +
    +
    + } + else + { + + The form submitted successfully. + + } +
    +
    + + +
    Stretch buttons or align content by combining FullWidth and ReversedIcon.
    +
    + + FullWidth + +
    + + FullWidth with reversed icon + +
    + + +
    Preview the predefined colors of the BitActionButton for icon and text, from primary through warning/error, and the background/foreground/border sets.

    - Primary - Primary + + Primary + + + Primary +

    - Secondary - Secondary + + Secondary + + + Secondary +

    - Tertiary - Tertiary + + Tertiary + + + Tertiary +

    - Info - Info + + Info + + + Info +

    - Success - Success + + Success + + + Success +

    - Warning - Warning + + Warning + + + Warning +

    - SevereWarning - SevereWarning + + SevereWarning + + + SevereWarning +

    - Error - Error + + Error + + + Error +


    - PrimaryBackground - PrimaryBackground + + PrimaryBackground + + + PrimaryBackground +

    - SecondaryBackground - SecondaryBackground + + SecondaryBackground + + + SecondaryBackground +

    - TertiaryBackground - TertiaryBackground + + TertiaryBackground + + + TertiaryBackground +

    - PrimaryForeground - PrimaryForeground + + PrimaryForeground + + + PrimaryForeground +

    - SecondaryForeground - SecondaryForeground + + SecondaryForeground + + + SecondaryForeground +

    - TertiaryForeground - TertiaryForeground + + TertiaryForeground + + + TertiaryForeground +


    - PrimaryBorder - PrimaryBorder + + PrimaryBorder + + + PrimaryBorder +

    - SecondaryBorder - SecondaryBorder + + SecondaryBorder + + + SecondaryBorder +

    - TertiaryBorder - TertiaryBorder + + TertiaryBorder + + + TertiaryBorder +
    - -
    Show BitActionButton in different sizes: Small, Medium, and Large.
    + +
    Compare the Small, Medium, and Large presets to gauge typography and padding differences.

    - Small - Medium - Large + + Small + + + Medium + + + Large +
    - -
    Customize the appearance of BitActionButton using styles and CSS classes.
    + +
    Override look-and-feel with inline styles or custom CSS classes applied to the root, icon, and content.

    + Icon = "color: blueviolet;", + Content = "text-shadow: aqua 0 0 1rem;" })"> Action Button Styles + Icon = "custom-icon", + Content = "custom-content" })"> Action Button Classes (Hover me)
    - -
    Add custom template within BitActionButton.
    + +
    View ActionButton inside a right-to-left layout, respecting RTL direction and icon/text ordering.

    -
    - -
    -
    This is a custom template
    - -
    +
    + + ساخت حساب
    + - -
    Use BitActionButton within a form and validate its state.
    -
    -
    - @if (formIsValidSubmit is false) - { - - - - - -
    - -
    -
    - Submit - Reset - Button -
    -
    - } - else - { - - The form submitted successfully. - - } -
    -
    - - -
    Use BitActionButton in right-to-left (RTL).
    -
    -
    - ساخت حساب -
    -
    - \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.cs index 94150ecbc1..13a40e3c0a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.cs @@ -9,28 +9,28 @@ public partial class BitActionButtonDemo Name = "AllowDisabledFocus", Type = "bool", DefaultValue = "false", - Description = "Whether the button can have focus in disabled mode.", + Description = "Keeps the disabled action button focusable by not forcing a negative tabindex when IsEnabled is false.", }, new() { Name = "AriaDescription", Type = "string?", DefaultValue = "null", - Description = "Detailed description of the button for the benefit of screen readers.", + Description = "Detailed description of the button for the benefit of screen readers (rendered into aria-describedby).", }, new() { Name = "AriaHidden", Type = "bool", DefaultValue = "false", - Description = "If true, add an aria-hidden attribute instructing screen readers to ignore the button.", + Description = "If true, adds an aria-hidden attribute instructing screen readers to ignore the button.", }, new() { Name = "ButtonType", Type = "BitButtonType", DefaultValue = "null", - Description = "The type html attribute of the button element.", + Description = "The type of the button element; defaults to submit inside an EditForm otherwise button.", LinkType = LinkType.Link, Href = "#button-type-enum", }, @@ -39,14 +39,14 @@ public partial class BitActionButtonDemo Name = "ChildContent", Type = "RenderFragment?", DefaultValue = "null", - Description = "The content of the button.", + Description = "The custom body of the action button (text and/or any render fragment).", }, new() { Name = "Classes", Type = "BitActionButtonClassStyles?", DefaultValue = "null", - Description = "Custom CSS classes for different parts of the button.", + Description = "Custom CSS classes for the root, icon, and content sections of the action button.", LinkType = LinkType.Link, Href = "#class-styles", }, @@ -55,11 +55,18 @@ public partial class BitActionButtonDemo Name = "Color", Type = "BitColor?", DefaultValue = "null", - Description = "The general color of the button.", + Description = "The general color of the button that applies to the icon and text of the action button.", LinkType = LinkType.Link, Href = "#color-enum", }, new() + { + Name = "EditContext", + Type = "EditContext?", + DefaultValue = "null", + Description = "The EditContext, which is set if the button is inside an EditForm. The value is coming from the cascading value provided by the EditForm.", + }, + new() { Name = "FullWidth", Type = "bool", @@ -78,27 +85,29 @@ public partial class BitActionButtonDemo Name = "IconName", Type = "string?", DefaultValue = "null", - Description = "The icon name of the icon to render inside the button.", + Description = "The Fluent UI icon name to render inside the action button (e.g., BitIconName.AddFriend). Browse available names in BitIconName of the Bit.BlazorUI.Icons nuget package or the gallery: https://blazorui.bitplatform.dev/iconography.", + LinkType = LinkType.Link, + Href = "https://blazorui.bitplatform.dev/iconography", }, new() { Name = "IconOnly", Type = "bool", - DefaultValue = "null", + DefaultValue = "false", Description = "Removes the container of the text and only renders the icon.", }, new() { Name = "OnClick", Type = "EventCallback", - Description = "The callback for the click event of the button.", + Description = "Raised when the action button is clicked (only when IsEnabled is true); receives MouseEventArgs.", }, new() { Name = "Styles", Type = "BitActionButtonClassStyles?", DefaultValue = "null", - Description = "Custom CSS styles for different parts of the button.", + Description = "Custom inline styles for the root, icon, and content sections of the action button.", LinkType = LinkType.Link, Href = "#class-styles", }, @@ -107,14 +116,14 @@ public partial class BitActionButtonDemo Name = "ReversedIcon", Type = "bool", DefaultValue = "false", - Description = "Reverses the positions of the icon and the content of the button.", + Description = "Swaps the order of the icon and content so the icon appears after the text.", }, new() { Name = "Rel", Type = "BitLinkRel?", DefaultValue = "null", - Description = "If Href provided, specifies the relationship between the current document and the linked document.", + Description = "Sets the rel attribute for link-rendered buttons when Href is a non-anchor URL; ignored for empty or hash-only hrefs. The rel attribute specifies the relationship between the current document and the linked document.", LinkType = LinkType.Link, Href = "#button-rel", }, @@ -123,7 +132,7 @@ public partial class BitActionButtonDemo Name = "Size", Type = "BitSize?", DefaultValue = "null", - Description = "The size of the button.", + Description = "Sets the preset size for typography and padding of the action button.", LinkType = LinkType.Link, Href = "#size-enum", }, @@ -149,6 +158,7 @@ public partial class BitActionButtonDemo { Id = "class-styles", Title = "BitActionButtonClassStyles", + Description = "Defines per-part CSS class/style values for BitActionButton.", Parameters = [ new() @@ -156,21 +166,21 @@ public partial class BitActionButtonDemo Name = "Root", Type = "string?", DefaultValue = "null", - Description = "Custom CSS classes/styles for the root element of the BitActionButton." + Description = "Custom class or style applied to the root element of the BitActionButton." }, new() { Name = "Icon", Type = "string?", DefaultValue = "null", - Description = "Custom CSS classes/styles for the Icon of the BitActionButton." + Description = "Custom class or style applied to the icon element of the BitActionButton." }, new() { Name = "Content", Type = "string?", DefaultValue = "null", - Description = "Custom CSS classes/styles for the content of the BitActionButton." + Description = "Custom class or style applied to the content container of the BitActionButton." } ] } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.samples.cs index 44d9338698..44e3c14f21 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.samples.cs @@ -3,14 +3,32 @@ public partial class BitActionButtonDemo { private readonly string example1RazorCode = @" -Create account -Reversed Icon -Disabled -No Icon"; + + Create account + + + + Reversed Icon + + + + Disabled + + + + AriaLabel + + + + No Icon + + + +"; private readonly string example2RazorCode = @" - Open bitplatform.dev + Open bitplatform.dev in a new tab @@ -18,72 +36,208 @@ Go to bitplatform GitHub "; private readonly string example3RazorCode = @" - + Open bitplatform.dev with a rel attribute (nofollow) - + Open bitplatform.dev with a rel attribute (nofollow & noreferrer) "; private readonly string example4RazorCode = @" -Primary -Primary + +
    + This is a custom template + +
    +
    "; -Secondary -Secondary + private readonly string example5RazorCode = @" + + + + + + + validationButtonModel.RequiredText"" style=""color:red"" /> + + + +
    + + Submit + + + Reset + + + Button + +
    +
    "; + private readonly string example5CsharpCode = @" +public class ButtonValidationModel +{ + [Required] + public string RequiredText { get; set; } = string.Empty; -Tertiary -Tertiary + public string? NonRequiredText { get; set; } +} -Info -Info +private ButtonValidationModel validationButtonModel = new(); -Success -Success +private async Task HandleValidSubmit() +{ + await Task.Delay(2000); -Warning -Warning + validationButtonModel = new(); -SevereWarning -SevereWarning + StateHasChanged(); +}"; -Error -Error + private readonly string example6RazorCode = @" + + FullWidth + -PrimaryBackground -PrimaryBackground + + FullWidth with reversed icon +"; -SecondaryBackground -SecondaryBackground + private readonly string example7RazorCode = @" + + Primary + + + Primary + -TertiaryBackground -TertiaryBackground + + Secondary + + + Secondary + -PrimaryForeground -PrimaryForeground + + Tertiary + + + Tertiary + -SecondaryForeground -SecondaryForeground + + Info + + + Info + -TertiaryForeground -TertiaryForeground + + Success + + + Success + -PrimaryBorder -PrimaryBorder + + Warning + + + Warning + -SecondaryBorder -SecondaryBorder + + SevereWarning + + + SevereWarning + -TertiaryBorder -TertiaryBorder"; + + Error + + + Error + - private readonly string example5RazorCode = @" -Small -Medium -Large"; + + PrimaryBackground + + + PrimaryBackground + - private readonly string example6RazorCode = @" + + SecondaryBackground + + + SecondaryBackground + + + + TertiaryBackground + + + TertiaryBackground + + + + PrimaryForeground + + + PrimaryForeground + + + + SecondaryForeground + + + SecondaryForeground + + + + TertiaryForeground + + + TertiaryForeground + + + + PrimaryBorder + + + PrimaryBorder + + + + SecondaryBorder + + + SecondaryBorder + + + + TertiaryBorder + + + TertiaryBorder +"; + + private readonly string example8RazorCode = @" + + Small + + + + Medium + + + + Large +"; + + private readonly string example9RazorCode = @"