From 56cea8c6caf8dae43d84066789c995409ee0a7e6 Mon Sep 17 00:00:00 2001 From: albx Date: Sat, 11 Jul 2026 17:19:16 +0200 Subject: [PATCH 01/15] #54 - start implementation of dropdown --- .../Components/Dropdown/BitDropdown.razor | 24 ++++++++++ .../Components/Dropdown/BitDropdown.razor.cs | 48 +++++++++++++++++++ .../Components/Dropdown/BitDropdownItem.razor | 7 +++ .../Dropdown/BitDropdownItem.razor.cs | 21 ++++++++ .../Components/Dropdown/DropdownState.cs | 10 ++++ .../Components/BitDropdown.stories.razor | 15 ++++++ .../Dropdown/BitDropdownTest.Rendering.razor | 37 ++++++++++++++ 7 files changed, 162 insertions(+) create mode 100644 src/BitBlazor/Components/Dropdown/BitDropdown.razor create mode 100644 src/BitBlazor/Components/Dropdown/BitDropdown.razor.cs create mode 100644 src/BitBlazor/Components/Dropdown/BitDropdownItem.razor create mode 100644 src/BitBlazor/Components/Dropdown/BitDropdownItem.razor.cs create mode 100644 src/BitBlazor/Components/Dropdown/DropdownState.cs create mode 100644 stories/BitBlazor.Stories/Components/Stories/Components/BitDropdown.stories.razor create mode 100644 tests/BitBlazor.Test/Components/Dropdown/BitDropdownTest.Rendering.razor diff --git a/src/BitBlazor/Components/Dropdown/BitDropdown.razor b/src/BitBlazor/Components/Dropdown/BitDropdown.razor new file mode 100644 index 0000000..be66073 --- /dev/null +++ b/src/BitBlazor/Components/Dropdown/BitDropdown.razor @@ -0,0 +1,24 @@ +@namespace BitBlazor.Components + +@inherits BitComponentBase + +
+ @DefaultActivator +
+ +
+
+ +@code { + private RenderFragment DefaultActivator => + @; +} diff --git a/src/BitBlazor/Components/Dropdown/BitDropdown.razor.cs b/src/BitBlazor/Components/Dropdown/BitDropdown.razor.cs new file mode 100644 index 0000000..18c51aa --- /dev/null +++ b/src/BitBlazor/Components/Dropdown/BitDropdown.razor.cs @@ -0,0 +1,48 @@ +using BitBlazor.Components.Dropdown; +using BitBlazor.Core; +using Microsoft.AspNetCore.Components; + +namespace BitBlazor.Components; + +/// +/// Represents a dropdown component that can be used to display a list of options or actions in a collapsible menu. +/// +public partial class BitDropdown : BitComponentBase +{ + /// + /// Gets or sets the content to be rendered inside the dropdown. This content can include a list of components. + /// + [Parameter] + [EditorRequired] + public RenderFragment ChildContent { get; set; } + + /// + /// Gets or sets the label for the dropdown activator button. This label is displayed on the button that triggers the dropdown menu. + /// + [Parameter] + public string ActivatorLabel { get; set; } = string.Empty; + + private DropdownState state = new(); + + private string AriaExpanded => state.IsOpen ? "true" : "false"; + + private string ComputeDropdownContainerClass() + { + var builder = new CssClassBuilder("dropdown"); + + AddCustomCssClass(builder); + + return builder.Build(); + } + + private string ComputeDropdownMenuClass() + { + var builder = new CssClassBuilder("dropdown-menu"); + if (state.IsOpen) + { + builder.Add("show"); + } + + return builder.Build(); + } +} diff --git a/src/BitBlazor/Components/Dropdown/BitDropdownItem.razor b/src/BitBlazor/Components/Dropdown/BitDropdownItem.razor new file mode 100644 index 0000000..8406803 --- /dev/null +++ b/src/BitBlazor/Components/Dropdown/BitDropdownItem.razor @@ -0,0 +1,7 @@ +@namespace BitBlazor.Components + +
  • + + @ChildContent + +
  • diff --git a/src/BitBlazor/Components/Dropdown/BitDropdownItem.razor.cs b/src/BitBlazor/Components/Dropdown/BitDropdownItem.razor.cs new file mode 100644 index 0000000..cfa395a --- /dev/null +++ b/src/BitBlazor/Components/Dropdown/BitDropdownItem.razor.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Components; + +namespace BitBlazor.Components; + +public partial class BitDropdownItem +{ + [CascadingParameter] + BitDropdown Parent { get; set; } = default!; + + [Parameter] + public RenderFragment ChildContent { get; set; } + + /// + protected override void OnInitialized() + { + if (Parent is null) + { + throw new InvalidOperationException("BitDropdownItem component must be used inside a BitDropdown component"); + } + } +} diff --git a/src/BitBlazor/Components/Dropdown/DropdownState.cs b/src/BitBlazor/Components/Dropdown/DropdownState.cs new file mode 100644 index 0000000..c5ea2e4 --- /dev/null +++ b/src/BitBlazor/Components/Dropdown/DropdownState.cs @@ -0,0 +1,10 @@ +namespace BitBlazor.Components.Dropdown; + +public class DropdownState +{ + private bool _isOpen; + + public bool IsOpen => _isOpen; + + public void Toggle() => _isOpen = !_isOpen; +} diff --git a/stories/BitBlazor.Stories/Components/Stories/Components/BitDropdown.stories.razor b/stories/BitBlazor.Stories/Components/Stories/Components/BitDropdown.stories.razor new file mode 100644 index 0000000..64325f8 --- /dev/null +++ b/stories/BitBlazor.Stories/Components/Stories/Components/BitDropdown.stories.razor @@ -0,0 +1,15 @@ +@attribute [Stories("Components/BitDropdown")] + + + + + + diff --git a/tests/BitBlazor.Test/Components/Dropdown/BitDropdownTest.Rendering.razor b/tests/BitBlazor.Test/Components/Dropdown/BitDropdownTest.Rendering.razor new file mode 100644 index 0000000..57769cf --- /dev/null +++ b/tests/BitBlazor.Test/Components/Dropdown/BitDropdownTest.Rendering.razor @@ -0,0 +1,37 @@ +@inherits BunitContext + +@code { + [Fact] + public void BitDropdown_Should_Render_Default_Markup_Correctly() + { + var component = Render( + @ + Action 1 + Action 2 + Action 3 + ); + + component.MarkupMatches( + @); + } +} From a91c8a2538c00f45ed9a013d6e985f9fe3d48258 Mon Sep 17 00:00:00 2001 From: albx Date: Sat, 11 Jul 2026 18:57:07 +0200 Subject: [PATCH 02/15] #54 - add position classes --- .../Components/Dropdown/BitDropdown.razor | 2 +- .../Components/Dropdown/BitDropdown.razor.cs | 26 ++++++++++- .../Components/Dropdown/DropdownPosition.cs | 27 +++++++++++ .../Components/Dropdown/DropdownState.cs | 9 ++++ .../Components/BitDropdown.stories.razor | 46 ++++++++++++++++++- .../Dropdown/BitDropdownTest.Rendering.razor | 40 +++++++++++++++- 6 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 src/BitBlazor/Components/Dropdown/DropdownPosition.cs diff --git a/src/BitBlazor/Components/Dropdown/BitDropdown.razor b/src/BitBlazor/Components/Dropdown/BitDropdown.razor index be66073..67f1117 100644 --- a/src/BitBlazor/Components/Dropdown/BitDropdown.razor +++ b/src/BitBlazor/Components/Dropdown/BitDropdown.razor @@ -17,7 +17,7 @@ @code { private RenderFragment DefaultActivator => - @; diff --git a/src/BitBlazor/Components/Dropdown/BitDropdown.razor.cs b/src/BitBlazor/Components/Dropdown/BitDropdown.razor.cs index 18c51aa..31393f3 100644 --- a/src/BitBlazor/Components/Dropdown/BitDropdown.razor.cs +++ b/src/BitBlazor/Components/Dropdown/BitDropdown.razor.cs @@ -22,19 +22,43 @@ public partial class BitDropdown : BitComponentBase [Parameter] public string ActivatorLabel { get; set; } = string.Empty; - private DropdownState state = new(); + /// + /// Gets or sets the position of the dropdown menu relative to its toggle button. + /// + /// + /// The default value is , which positions the menu below the button. + /// Other options include , , and . + /// + [Parameter] + public DropdownPosition Position { get; set; } = DropdownPosition.Down; + + private readonly DropdownState state = new(); private string AriaExpanded => state.IsOpen ? "true" : "false"; private string ComputeDropdownContainerClass() { var builder = new CssClassBuilder("dropdown"); + AddPositionClass(builder); AddCustomCssClass(builder); return builder.Build(); } + private void AddPositionClass(CssClassBuilder builder) + { + var positionClass = Position switch + { + DropdownPosition.Up => "dropup", + DropdownPosition.End => "dropend", + DropdownPosition.Start => "dropstart", + _ => string.Empty + }; + + builder.Add(positionClass); + } + private string ComputeDropdownMenuClass() { var builder = new CssClassBuilder("dropdown-menu"); diff --git a/src/BitBlazor/Components/Dropdown/DropdownPosition.cs b/src/BitBlazor/Components/Dropdown/DropdownPosition.cs new file mode 100644 index 0000000..16ae8fd --- /dev/null +++ b/src/BitBlazor/Components/Dropdown/DropdownPosition.cs @@ -0,0 +1,27 @@ +namespace BitBlazor.Components; + +/// +/// Represents the position of the dropdown menu relative to its toggle button. +/// +public enum DropdownPosition +{ + /// + /// The dropdown menu is positioned below the toggle button. Default value. + /// + Down, + + /// + /// The dropdown menu is positioned above the toggle button. + /// + Up, + + /// + /// The dropdown menu is positioned to the right of the toggle button. + /// + End, + + /// + /// The dropdown menu is positioned to the left of the toggle button. + /// + Start +} diff --git a/src/BitBlazor/Components/Dropdown/DropdownState.cs b/src/BitBlazor/Components/Dropdown/DropdownState.cs index c5ea2e4..0ec9475 100644 --- a/src/BitBlazor/Components/Dropdown/DropdownState.cs +++ b/src/BitBlazor/Components/Dropdown/DropdownState.cs @@ -1,10 +1,19 @@ namespace BitBlazor.Components.Dropdown; +/// +/// Represents the state of a dropdown component, including whether it is open or closed. +/// public class DropdownState { private bool _isOpen; + /// + /// Gets a value indicating whether the dropdown is currently open. If true, the dropdown is open; otherwise, it is closed. + /// public bool IsOpen => _isOpen; + /// + /// Toggles the open/closed state of the dropdown. If the dropdown is currently open, calling this method will close it, and vice versa. + /// public void Toggle() => _isOpen = !_isOpen; } diff --git a/stories/BitBlazor.Stories/Components/Stories/Components/BitDropdown.stories.razor b/stories/BitBlazor.Stories/Components/Stories/Components/BitDropdown.stories.razor index 64325f8..da0b4e5 100644 --- a/stories/BitBlazor.Stories/Components/Stories/Components/BitDropdown.stories.razor +++ b/stories/BitBlazor.Stories/Components/Stories/Components/BitDropdown.stories.razor @@ -1,10 +1,54 @@ @attribute [Stories("Components/BitDropdown")] + + + + + + + + + + + + + + + + + + + +