Skip to content

feat(components): introduce Spinner component#207

Merged
desmondinho merged 7 commits intodevfrom
feat/spinner
Mar 16, 2025
Merged

feat(components): introduce Spinner component#207
desmondinho merged 7 commits intodevfrom
feat/spinner

Conversation

@desmondinho
Copy link
Contributor

@desmondinho desmondinho commented Mar 16, 2025

Description

Closes #178

This PR introduces the new Spinner component.

What's been done?

  • Added the new Spinner component

Checklist

  • My code follows the project's coding style and guidelines.
  • I have included inline docs for my changes, where applicable.
  • I have added, updated or removed tests according to my changes.
  • All tests are passing.
  • There's an open issue for the PR that I am making.

Additional Notes

Summary by CodeRabbit

  • New Features
    • Introduced a new, versatile loading spinner with multiple visual variants offering different sizes, color options, and labeling.
  • Documentation
    • Expanded interactive guides with comprehensive demos and examples showcasing the spinner’s usage and variants, along with improved navigation.
  • Style
    • Refined styling with enhanced animations, horizontal overflow handling, and visual consistency for a polished user interface.
  • Chores
    • Optimized resource integration and build configurations for more efficient static asset management.

@desmondinho desmondinho added 📦 Scope: Components Improvements or additions to components 🚀 Type: Feature labels Mar 16, 2025
@desmondinho desmondinho self-assigned this Mar 16, 2025
@desmondinho desmondinho linked an issue Mar 16, 2025 that may be closed by this pull request
1 task
@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2025

Walkthrough

This pull request introduces the new LumexSpinner component along with its supporting examples, documentation, and tests. Updates include additions to the navigation store, new Razor component examples demonstrating spinner variants (colors, labels, sizes, usage, and variants), interactive preview components using WebAssembly, and accompanying styles, enums, utilities, and tests. Minor updates to documentation assets and middleware configuration are also included.

Changes

File(s) Change Summary
docs/.../Common/Navigation/NavigationStore.cs Added new LumexSpinner entries to both ComponentsCategory (with ComponentStatus.New) and ComponentsApiCategory.
docs/.../Components/Preview.razor Updated CSS classes for the preview element to include horizontal overflow handling and hide the scrollbar.
docs/.../Pages/Components/Skeleton/Skeleton.razor Removed the "Composition" heading entry from the headings array.
docs/.../Pages/Components/Spinner/Examples/{Colors, Label, Sizes, Usage, Variants}.razor Introduced multiple examples of the LumexSpinner component demonstrating color themes, labeled spinner, size variations, basic usage, and different visual variants.
docs/.../Pages/Components/Spinner/PreviewCodes/{Colors, Label, Sizes, Usage, Variants}.razor Added interactive preview components using @rendermode InteractiveWebAssembly for each spinner example.
docs/.../Pages/Components/Spinner/Spinner.razor Added a comprehensive documentation page for LumexSpinner including sections for usage, labeling, sizing, coloring, variants, and API details.
docs/.../Components/App.razor Replaced the link to css/docs-tw4.css with css/docs.css, removed body class attributes, and added an <ImportMap /> component.
docs/.../LumexUI.Docs.csproj Replaced the old TailwindCSS target with two new targets (TailwindCSS-Dev and TailwindCSS-Prod) that compile Tailwind CSS based on the build configuration.
docs/.../Program.cs Updated middleware configuration by replacing app.UseStaticFiles() with app.MapStaticAssets().
src/.../Common/Enums/SpinnerVariant.cs Added a new SpinnerVariant enum defining the visual variants: Ring, Arc, ArcGradient, DotsWave, DotsFade, and Classic.
src/.../Components/Spinner/{LumexSpinner.razor, LumexSpinner.razor.cs} Introduced the LumexSpinner component with support for multiple variants, sizes, labels, and customizable styling via parameters and slot configuration.
src/.../Components/Spinner/SpinnerSlots.cs Added the SpinnerSlots class to define CSS slot names for various parts of the spinner (e.g., Base, Wrapper, Label, etc.).
src/.../Styles/Spinner.cs Added a static class that generates Tailwind CSS-based styles for the spinner including size, color, and compound variant configurations.
src/.../Styles/_theme.css Extended the base CSS layer; added new animation properties and keyframes for sway, blink, and fade-out.
src/.../Utilities/Variants/TwVariants.cs Updated access modifiers from public to internal and simplified the implementation for merging component slot classes.
tests/.../Components/Spinner/SpinnerTests.razor Added unit tests for LumexSpinner validating rendering behavior and aria-label accessibility in various conditions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DocsPage
    participant NavigationStore
    participant PreviewCodes
    participant LumexSpinner

    User->>DocsPage: Navigate to Documentation
    DocsPage->>NavigationStore: Request navigation items
    NavigationStore-->>DocsPage: Return items (includes LumexSpinner)
    User->>DocsPage: Select Spinner documentation
    DocsPage->>PreviewCodes: Load spinner preview (Usage/Sizes/etc.)
    PreviewCodes->>LumexSpinner: Render spinner with chosen variant and settings
    LumexSpinner-->>PreviewCodes: Return rendered UI component
    PreviewCodes-->>DocsPage: Display interactive spinner preview
    DocsPage-->>User: Show updated spinner component
Loading

Assessment against linked issues

Objective Addressed Explanation
#178: Add Spinner component

Possibly related PRs

Poem

Hop, hop, in code I roam,
Spinners twirl to guide you home.
With styles and slots, I’m spinning free,
Each variant shines so marvelously.
A rabbit’s cheer in every tweak—hop on, let’s geek!
🐇💻

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@desmondinho desmondinho merged commit 19261d2 into dev Mar 16, 2025
3 of 4 checks passed
@desmondinho desmondinho deleted the feat/spinner branch March 16, 2025 20:20
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (8)
docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Label.razor (1)

5-6: Remove trailing empty line.

There appears to be an extra empty line at the end of the file that's not needed.

 <PreviewCode Code="@new(name: null, snippet: "Spinner.Code.Label")">
     <LumexUI.Docs.Client.Pages.Components.Spinner.Examples.Label />
 </PreviewCode>
-
docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Colors.razor (1)

5-6: Remove trailing empty line.

There appears to be an extra empty line at the end of the file that's not needed.

 <PreviewCode Code="@new(name: null, snippet: "Spinner.Code.Colors")">
     <LumexUI.Docs.Client.Pages.Components.Spinner.Examples.Colors />
 </PreviewCode>
-
src/LumexUI/Styles/Spinner.cs (1)

259-545: Consider refactoring compound variants to reduce repetition.

The compound variants section contains repetitive patterns, especially for color variants. Consider using a helper method or a more concise approach to reduce code duplication.

Example of a potential helper method approach:

// Add a helper method to generate color compound variants
+ private static CompoundVariant CreateColorCompoundVariant(string variant, string color, string slotName, string textOrBorderClass)
+ {
+     return new CompoundVariant()
+     {
+         Conditions = new()
+         {
+             [nameof(LumexSpinner.Variant)] = variant,
+             [nameof(LumexSpinner.Color)] = color
+         },
+         Classes = new SlotCollection()
+         {
+             [slotName] = textOrBorderClass
+         }
+     };
+ }

// Then use it to generate variants
// For Ring variant colors:
+ var ringColorVariants = new List<CompoundVariant>();
+ foreach (var color in Enum.GetNames(typeof(ThemeColor)))
+ {
+     if (color == nameof(ThemeColor.None)) continue;
+     ringColorVariants.Add(CreateColorCompoundVariant(
+         nameof(SpinnerVariant.Ring),
+         color,
+         nameof(SpinnerSlots.Wrapper), 
+         $"text-{color.ToLowerInvariant()}"
+     ));
+ }
tests/LumexUI.Tests/Components/Spinner/SpinnerTests.razor (1)

1-61: Consider adding tests for size, color, and child content.

While the current tests cover variants and accessibility well, consider adding tests for:

  1. Size parameter to verify that different sizes are applied correctly
  2. Color parameter to ensure colors are applied
  3. Child content rendering to verify the label displays the content properly
[Theory]
[InlineData(Size.Small)]
[InlineData(Size.Medium)]
[InlineData(Size.Large)]
public void ShouldApplySizeCorrectly(Size size)
{
    var cut = Render(
        @<LumexSpinner Size="@size" />
    );
    
    var wrapper = cut.FindBySlot("wrapper");
    
    // Assert size classes are applied based on the size parameter
    // This would depend on your exact implementation details
    if (size == Size.Small)
    {
        wrapper!.ClassList.Should().Contain(c => c.Contains("size-5"));
    }
    else if (size == Size.Medium)
    {
        wrapper!.ClassList.Should().Contain(c => c.Contains("size-8"));
    }
    else if (size == Size.Large)
    {
        wrapper!.ClassList.Should().Contain(c => c.Contains("size-10") || c.Contains("size-12"));
    }
}

[Theory]
[InlineData(ThemeColor.Primary)]
[InlineData(ThemeColor.Secondary)]
[InlineData(ThemeColor.Success)]
public void ShouldApplyColorCorrectly(ThemeColor color)
{
    var cut = Render(
        @<LumexSpinner Color="@color" />
    );
    
    // Assert color is applied appropriately
    // The exact assertion would depend on your implementation
}

[Fact]
public void ShouldRenderChildContent()
{
    var childContent = "Loading Data...";
    
    var cut = Render(
        @<LumexSpinner>@childContent</LumexSpinner>
    );
    
    var label = cut.FindBySlot("label");
    
    label!.TextContent.Should().Be(childContent);
}
src/LumexUI/Components/Spinner/SpinnerSlots.cs (2)

39-47: Documentation requires correction for Circle1 and Circle2 properties.

The XML documentation for the Circle1 and Circle2 properties incorrectly states they are for the "wrapper slot" rather than their respective slots.

- /// Gets or sets the CSS class for the wrapper slot.
+ /// Gets or sets the CSS class for the circle1 slot.
- /// Gets or sets the CSS class for the wrapper slot.
+ /// Gets or sets the CSS class for the circle2 slot.

49-57: Documentation requires correction for Dots and Bars properties.

The XML documentation for the Dots and Bars properties incorrectly states they are for the "wrapper slot" rather than their respective slots.

- /// Gets or sets the CSS class for the wrapper slot.
+ /// Gets or sets the CSS class for the dots slot.
- /// Gets or sets the CSS class for the wrapper slot.
+ /// Gets or sets the CSS class for the bars slot.
src/LumexUI/Components/Spinner/LumexSpinner.razor.cs (2)

73-73: XML reference has incorrect class name.

The XML documentation references LumexAvatar instead of LumexSpinner in the constructor's documentation.

- /// Initializes a new instance of the <see cref="LumexAvatar"/>.
+ /// Initializes a new instance of the <see cref="LumexSpinner"/>.

93-112: Consider more graceful error handling for GetStyles method.

The current implementation throws NotImplementedException for missing slots. Consider returning a default or empty value instead, or providing a more specific exception message to help with debugging.

private string? GetStyles( string slot )
{
    if( !_slots.TryGetValue( slot, out var styles ) )
    {
-        throw new NotImplementedException();
+        throw new ArgumentException($"Slot '{slot}' is not implemented in LumexSpinner component.");
    }

    return slot switch
    {
        nameof( SpinnerSlots.Base ) => styles( Classes?.Base, Class ),
        nameof( SpinnerSlots.Wrapper ) => styles( Classes?.Wrapper ),
        nameof( SpinnerSlots.Label ) => styles( Classes?.Label ),
        nameof( SpinnerSlots.Circle1 ) => styles( Classes?.Circle1 ),
        nameof( SpinnerSlots.Circle2 ) => styles( Classes?.Circle2 ),
        nameof( SpinnerSlots.Dots ) => styles( Classes?.Dots ),
        nameof( SpinnerSlots.Bars ) => styles( Classes?.Bars ),
-        _ => throw new NotImplementedException()
+        _ => throw new ArgumentException($"Style application for slot '{slot}' is not implemented in LumexSpinner component.")
    };
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42297ea and 9c12bcc.

📒 Files selected for processing (25)
  • docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs (2 hunks)
  • docs/LumexUI.Docs.Client/Components/Preview.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Skeleton/Skeleton.razor (0 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Colors.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Label.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Sizes.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Usage.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Variants.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Colors.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Label.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Sizes.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Usage.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Variants.razor (1 hunks)
  • docs/LumexUI.Docs.Client/Pages/Components/Spinner/Spinner.razor (1 hunks)
  • docs/LumexUI.Docs/Components/App.razor (1 hunks)
  • docs/LumexUI.Docs/LumexUI.Docs.csproj (1 hunks)
  • docs/LumexUI.Docs/Program.cs (1 hunks)
  • src/LumexUI/Common/Enums/SpinnerVariant.cs (1 hunks)
  • src/LumexUI/Components/Spinner/LumexSpinner.razor (1 hunks)
  • src/LumexUI/Components/Spinner/LumexSpinner.razor.cs (1 hunks)
  • src/LumexUI/Components/Spinner/SpinnerSlots.cs (1 hunks)
  • src/LumexUI/Styles/Spinner.cs (1 hunks)
  • src/LumexUI/Styles/_theme.css (3 hunks)
  • src/LumexUI/Utilities/Variants/TwVariants.cs (2 hunks)
  • tests/LumexUI.Tests/Components/Spinner/SpinnerTests.razor (1 hunks)
💤 Files with no reviewable changes (1)
  • docs/LumexUI.Docs.Client/Pages/Components/Skeleton/Skeleton.razor
🧰 Additional context used
🪛 GitHub Check: build-test
src/LumexUI/Common/Enums/SpinnerVariant.cs

[warning] 16-16:
Missing XML comment for publicly visible type or member 'SpinnerVariant.ArcGradient'


[warning] 14-14:
Missing XML comment for publicly visible type or member 'SpinnerVariant.Arc'


[warning] 12-12:
Missing XML comment for publicly visible type or member 'SpinnerVariant.Ring'

🪛 Biome (1.9.4)
src/LumexUI/Styles/_theme.css

[error] 1-1: expected , but instead found @

Remove @

(parse)


[error] 1-1: expected , but instead found layer

Remove layer

(parse)

🔇 Additional comments (46)
docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Usage.razor (1)

1-1: Good basic example implementation!

This provides a clear example of the simplest way to use the LumexSpinner component without any customization.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Label.razor (1)

1-1: Well-implemented label example!

This demonstrates how to use the LumexSpinner component with a label, providing a clear use case.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Label.razor (1)

1-5: Interactive preview implementation looks good.

The preview code is correctly set up with the InteractiveWebAssembly render mode and properly references the Label example.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Colors.razor (1)

1-5: Interactive preview implementation looks good.

The preview code is correctly set up with the InteractiveWebAssembly render mode and properly references the Colors example.

docs/LumexUI.Docs.Client/Components/Preview.razor (1)

27-27: LGTM! Good enhancement for overflow content.

The addition of overflow-x-auto and scrollbar-hide classes improves the user experience when preview content exceeds the container width.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Sizes.razor (1)

1-5: LGTM! Clear demonstration of size variants.

The example provides a concise demonstration of the available spinner sizes with appropriate spacing.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Sizes.razor (1)

1-5: LGTM! Follows the established pattern for interactive component previews.

The implementation correctly uses WebAssembly for interactivity and properly references the size examples.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Variants.razor (1)

1-8: Well-organized variants showcase with clear examples.

The implementation provides a comprehensive display of all spinner variants in a clean, responsive layout. The naming is descriptive and the consistent structure makes it easy to understand the available options.

docs/LumexUI.Docs/Program.cs (1)

42-42:

Details

❓ Verification inconclusive

Verify static assets handling for the Spinner component.

The change from UseStaticFiles() to MapStaticAssets() affects how static resources are served. Please ensure that all spinner component assets (CSS, JavaScript, images) are properly handled by this new method, especially if they have special loading requirements.


🏁 Script executed:

#!/bin/bash
# Check if MapStaticAssets is defined in the codebase and what files it might handle
rg -A 5 "MapStaticAssets" --type csharp

Length of output: 402


Attention: Verify Static Assets Mapping for Spinner Component

The change from using UseStaticFiles() to the custom MapStaticAssets() suggests a shift in how static files are served. Please review the implementation of MapStaticAssets() to ensure that it correctly handles all static assets—including those required by the spinner component (CSS, JavaScript, images)—especially if they have any special loading requirements.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/Examples/Colors.razor (1)

1-9: Comprehensive color theme examples with consistent implementation.

The code effectively showcases all color variants with a clear, consistent approach. Each spinner appropriately demonstrates both the spinner color and matching label color, providing excellent visual reference for implementation.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Usage.razor (1)

1-5: Effective use of interactive preview for the spinner component.

The implementation correctly sets up the WebAssembly interactive mode and appropriately uses the PreviewCode component to display both the rendered example and its corresponding code snippet.

Could you clarify the purpose of the name: null parameter in the Code object creation? Understanding whether this is intentional or if it should have a specific value would ensure proper functionality.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/PreviewCodes/Variants.razor (1)

1-5: Looks good - PreviewCode component setup is correct.

The implementation correctly uses InteractiveWebAssembly rendering mode and properly sets up the PreviewCode component to display spinner variants. The structure follows the established pattern for component examples in the LumexUI documentation.

docs/LumexUI.Docs/Components/App.razor (3)

8-8: CSS reference updated correctly.

The stylesheet reference has been updated to use the new CSS output file that will be generated by the updated TailwindCSS build targets.


11-11: Added ImportMap component.

The ImportMap component has been added to the head section, which is important for managing JavaScript module dependencies.


15-15:

Details

❓ Verification inconclusive

Simplified body tag styling.

The class attributes have been removed from the body tag. Verify that these styles are now applied through another mechanism to maintain consistent styling.


🏁 Script executed:

#!/bin/bash
# Check if the background and text styling is defined elsewhere
cat docs/LumexUI.Docs/Styles/globals.css | grep -E "body|bg-background|text-foreground"

Length of output: 89


I'm not seeing any relevant styles in globals.css based on the initial grep. To further verify if the background and text styling are defined elsewhere, please run the following script to recursively search the repository for occurrences of the keywords "bg-background" or "text-foreground":


🏁 Script executed:

#!/bin/bash
# Recursively search the repository for alternative styling definitions using the keywords "bg-background" or "text-foreground"
rg -E "bg-background|text-foreground" -n .

Length of output: 111


Action Required: Confirm Alternative Styling Application for <body>

Our initial verification using grep and rg did not reveal any explicit definitions for bg-background, text-foreground, or alternative styles for the <body> tag in the repository. Please manually verify that the intended background and text styles are applied through another mechanism (e.g., a global CSS configuration, a layout component, or a styling framework integration) so that the removal of class attributes does not lead to unintended styling regressions.

  • File Affected: docs/LumexUI.Docs/Components/App.razor (Line 15)
  • Environment to Check: Global CSS files, layout components, and any framework configuration (e.g., Tailwind CSS configuration) that may now be handling these styles.
docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs (2)

44-44: Correctly added Spinner to components category.

The LumexSpinner component has been properly added to the ComponentsCategory with the appropriate ComponentStatus.New flag, which will help users identify it as a new addition.


91-91: Correctly added Spinner to API category.

The LumexSpinner component has been properly added to the ComponentsApiCategory, following the established pattern for component documentation.

docs/LumexUI.Docs/LumexUI.Docs.csproj (2)

50-53: Good implementation of development TailwindCSS build configuration.

The TailwindCSS-Dev target is correctly set up to compile CSS without minification in Debug mode, making development easier with readable CSS output. The commands handle both Windows and non-Windows environments appropriately.


55-57: Good implementation of production TailwindCSS build configuration.

The TailwindCSS-Prod target correctly includes the --minify flag for production builds, which will optimize the CSS file size. The conditional commands for different operating systems are handled appropriately.

src/LumexUI/Components/Spinner/LumexSpinner.razor (5)

6-24: Well-structured conditional rendering for dot-based variants.

The conditional rendering for DotsWave and DotsFade variants is implemented cleanly. The use of a loop to generate the dot elements is efficient and maintainable.


25-51: Good SVG implementation for the Ring variant.

The Ring variant uses SVG effectively with proper viewBox, fill attributes, and accessibility. The use of data-slot attributes makes styling and testing easier.


52-70: Efficient implementation of the Classic spinner variant.

The Classic spinner variant uses a loop to generate 12 bar elements, with index-based styling. This approach is efficient and maintainable.


71-85: Default spinner implementation is clean and minimal.

The default spinner variant is simple and effective, using two circle elements. The consistent structure across all variants makes the component predictable and maintainable.


87-99: Good approach for conditional label rendering.

The RenderLabel method efficiently handles both string labels and child content. The null/empty check prevents rendering empty label containers.

docs/LumexUI.Docs.Client/Pages/Components/Spinner/Spinner.razor (4)

6-32: Well-organized documentation structure.

The documentation is logically organized into sections covering usage, label, sizes, colors, and variants, providing a comprehensive guide for developers.


34-42: Comprehensive slots documentation.

The slots section clearly documents all available styling hooks, making it easier for developers to customize the component.


44-76: Clear API reference and well-structured code section.

The API section and the headings array provide good navigation structure. Initialization in OnInitialized follows the pattern used in other component documentation.


77-86: Proper metadata initialization.

The layout initialization correctly sets up title, category, description, and other metadata, ensuring consistent documentation structure.

src/LumexUI/Styles/Spinner.cs (5)

14-23: Good singleton pattern implementation for styles.

The singleton pattern with lazy initialization is appropriately used for the component variant, which helps with performance.


24-70: Well-structured base styles for spinner slots.

The slot definitions are clear and comprehensive, covering all the visual elements needed for the different spinner variants.


72-158: Comprehensive variant styles for size and color.

The variant collections for size and color are well-organized and cover all the needed variations.


161-197: Good separation of label styling from spinner styling.

The LabelColor variant collection allows for independent styling of the label, which enhances flexibility.


199-257: Thorough implementation of spinner variants.

Each spinner variant has appropriate styles defined, with good attention to animation details.

tests/LumexUI.Tests/Components/Spinner/SpinnerTests.razor (3)

4-24: Good variant rendering tests.

The theory test with inline data covers all spinner variants to ensure they render correctly without exceptions.


26-36: Proper testing of default accessibility attributes.

Testing the default aria-label ensures the component remains accessible even without explicit label configuration.


38-60: Thorough testing of aria-label customization.

Tests for both label-based and attribute-based aria-label customization ensure the component handles accessibility properly.

src/LumexUI/Styles/_theme.css (4)

1-5: New CSS layer improves style encapsulation.

The addition of a base layer with body styles establishes a clean baseline for text and background colors. This provides consistent styling for the new spinner component.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: expected , but instead found @

Remove @

(parse)


[error] 1-1: expected , but instead found layer

Remove layer

(parse)


175-175: Animation timing function change affects visual behavior.

Changing from ease-out to ease-in will make animations start slowly and accelerate, rather than starting quickly and slowing down. Ensure this is the intended behavior since it will affect all components using this animation.


177-179: New animation properties support the Spinner component.

The addition of --animate-sway, --animate-blink, and --animate-fade-out properties provides the necessary animation patterns for the different spinner variants.


199-227: Well-defined keyframes for spinner animations.

The keyframe definitions for sway, blink, and fade-out animations are well structured and provide smooth, visually pleasing animation patterns that will enhance the loading experience.

src/LumexUI/Components/Spinner/SpinnerSlots.cs (2)

11-14: Documentation is clear and concise.

Good use of XML documentation to describe the purpose of the SpinnerSlots class, with a reference to LumexSpinner.


18-23: Correctly handling deprecated property.

The Root property is appropriately marked as obsolete with a clear explanation to use Base instead. Good practice for maintaining backward compatibility while guiding users toward the preferred API.

src/LumexUI/Utilities/Variants/TwVariants.cs (2)

5-5: Appropriate access restriction of utility components.

Changing the access modifiers from public to internal and adding the [ExcludeFromCodeCoverage] attribute is appropriate for utility components that should only be used within the library.

Also applies to: 11-16


114-114: Simplified method implementation is more readable.

The refactored CreateComponentSlot method uses a more concise array spread syntax for merging class names, making the code more readable while maintaining the same functionality.

src/LumexUI/Components/Spinner/LumexSpinner.razor.cs (2)

17-65: Well-designed component API with comprehensive documentation.

The LumexSpinner component provides a flexible API with appropriate defaults and comprehensive XML documentation. The parameters support customization of size, color, label, and variant, which aligns with modern component library best practices.


80-91: Parameter handling follows component design pattern.

The OnParametersSet method properly passes all relevant parameters to the styling system, ensuring that the spinner's appearance adapts based on the provided properties.

desmondinho added a commit that referenced this pull request Aug 24, 2025
* feat: support Tailwind CSS v4 (#183)

* build(deps): bump TailwindMerge.NET from 0.3.0 to 1.0.0

* feat: add new CSS theme file

* feat/build: add custom targets file to improve library usability

* chore: move `Plugin` folder one level higher; remove `Scripts` folder

* feat/build: pack new theme and custom `.targets` files

* build(docs): add `Directory.Build.props` and `Directory.Build.targets`

* chore(docs): remove tailwind npm deps; use standalone CLI instead

* docs: apply `static` on theme

* refactor(theme-provider): simplify names of box-shadow css variables

* refactor(theme-provider): opacities are percentage to match `color-mix` function syntax

* chore(components): apply  `static` on theme; fix some vars

* refactor(components): drop Tailwind CSS v3 support

* feat(theme): add custom transition variables

* fix(theme): correct `default` color; add `default-foreground` color

* chore(theme): add reference for the custom transitions approach (it's not in docs afaik)

* fix: rename `shadow-sm` to `shadow-xs`

* fix: rename `rounded-sm` to `rounded-xs`

* fix: rename `rounded` to `rounded-sm`

* fix: rename `outline-none` to `outline-hidden`

* fix: rename `ring-1` to `ring`

* fix(button): add base `cursor-pointer` class

* docs: remove `children` custom variant in favor of `*`

* fix(theme): correct `enter` custom animation

* chore(components): cleanup styles

* fix(theme): add missing comma separator in custom transition vars

* docs: configure typography

* refactor(theme): simplify `scrollbar-hide` utility

* chore(theme): apply `inline` on theme

* refactor: replace `theme` function with CSS vars

* fix(components): correct scale/translate transitions

* feat(theme): update colors from hex to oklch

* docs(installation): update installation guide

* feat(theme): add leading CSS vars

* chore(docs): fix prose `<code>` tag ticks

* refactor(utils): remove hex luminance calculator

* docs(customization): update Theme and Colors pages

* docs(colors): remove 'common colors are not configurable' callout

* fix(checkbox): correct radius styles

* fix(data-grid): correct striped styles

* fix(input/select): correct label placement out transitions

* fix(input/select): correct outlined variant focus styles

* fix(input): add cursor-pointer style on the clear button

* fix(input/select): correct flat variant focus styles

* fix(docs): correct some component examples

* build(docs): adjust Tailwind standalone CLI file download for Linux

* build(docs): adjust Tailwind standalone CLI file download for Linux

* ci(deploy): try add staging env in the ci/cd

* ci(build-test): change trigger branch

* ci(deploy): update trigger branches

* ci(deploy): change env vars usage (test)

* ci: add deploy-dev.yml; revert deploy.yml

* ci(deploy): test staging

* chore(docs): nits

* chore(components): tweak styles of some components

* chore(docs): tweak some components examples

* chore: coderabbit comments

* ci: remove deploy-dev.yml

* fix(theme): remove extra shade (950) from the color scales for consistency in dark mode (#199)

* fix(theme): remove extra key (950) from the color scales for consistency in dark mode

* build(docs): explicitly set Tailwind v4.0.9

* feat(components): introduce Avatar and AvatarGroup components (#201)

* feat: add baseline implementation

* feat: add slots

* feat: add basic slots styles

* feat: add appearance params, such as `Color`, `Radius`, `Size`

* feat: add `Bordered` and `Disabled` params

* feat: add compound variants styles

* feat: apply slots styles

* docs: add baseline examples page

* feat: add `data-loaded` attribute on img

* feat: add `ShowFallback` parameter

* chore: fix compound style variants

* chore: set `showFallback` on after first render

* feat(utils): add implicit cast to string for the `ElementClass`

* feat: add LumexAvatarGroup component

* feat: take into account when LumexAvatar is rendered inside the LumexAvatarGroup

* feat: add `AvatarClasses` parameter in the avatar group component

* docs: add Avatar page

* build(docs): explicitly set Tailwind v4.0.9

* test: add tests for LumexAvatar and LumexAvatarGroup components

* chore: simplify condition for fallback render

* fix(docs): replace usages of `-foreground-950` CSS classes with `-foreground-900`

* fix(docs): remove `dark:prose-invert` CSS class until dark theme is properly configured

* feat(components): introduce Skeleton component (#202)

* feat(skeleton): add baseline implementation of the component

* feat(skeleton): add slots and styles

* feat(skeleton): add XML summaries

* fix(skeleton): return back `after` pseudo CSS classes to prevent flickering on state change

* docs(skeleton): add Skeleton page

* test(skeleton): add tests

* docs(skeleton): fix Loading example button text

* fix(navbar): add a check before toggling navbar menu on navigation (#204)

* feat(components): introduce Spinner component (#207)

* feat(spinner): add baseline implementation

* feat(spinner): add variants and styles

* feat(spinner): add slots

* docs(spinner): add Spinner page

* docs: nits

* test(spinner): add tests

* docs: map static assets

* build(docs): remove extra MSBuild target for the Tailwind prod build

* docs: revert static assets changes

* docs: update static assets usage

* Revert "docs: update static assets usage"

This reverts commit 94ae9ec.

* feat(components): introduce Chip component (#211)

* feat(chip): add baseline implementation

* feat(chip): add ChipVariant enum

* feat(chip): add appearance parameters and styles

* feat(chip): add AvatarContent parameter

* feat(chip): adjust paddings when chip has start/end content

* docs(chip): add Chip page

* feat(chip): add XML summaries

* test(chip): add tests

* chore(components): add missing XML documentation summaries

* feat(components): add new Badge component (#222)

* feat(badge): initial

* feat(badge): add badge slots

* feat(badge): add badge baseline implementation

* feat(badge): add base visual-related params

* feat(badge): add majority of badge styles

* feat(badge): add outline around badge

* feat(badge): add `Invisible` param to control badge visibility

* feat(badge): add `IsOneChar` param to make badge equilateral

* feat(badge): decrease badge dimensions if no content provided

* refactor(badge): rename `IsOneChar` param to `OneChar`

* fix(badge): use correct type for `Variant` param

* feat(badge): apply CSS classes directly to the badge slot

* fix(badge): ensure correct placement styles

* fix(badge): correct `Content` check condition

* fix(badge): allow null for content

* fix(badge): properly render Content as RenderFragment

* docs(badge): add Badge docs page

* test(badge): add tests

* test(badge): add more tests

* fix(badge): ensure `OneChar` param is taken into account

* fix(badge): fix one char switch

* chore: apply CodeRabbit suggestions

* build(deps): bump requests from 2.32.0 to 2.32.4 in /scripts (#219)

Bumps [requests](https://github.com/psf/requests) from 2.32.0 to 2.32.4.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](psf/requests@v2.32.0...v2.32.4)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(components): add Tooltip component (#224)

* feat(tooltip): initial

* refactor(popover): introduce PopoverWrapper to simplify open state

* fix(popover): ensure arrow is positioned correctly

* fix(popover): ensure popover closes on trigger click if already opened

* fix(popover): position flicker

* test(popover/dropdown): adjust tests

* refactor(popover): remove LastShown meta from popover service

* feat(tooltip): add baseline implementation

* feat(tooltip): add common visual-related parameters to pass into popover

* feat(tooltip): add OpenDelay and CloseDelay params

* docs(tooltip): add Tooltip page

* feat(tooltip): pass slots to popover

* chore(popover): add full radius styles

* test(tooltip): add tests

* docs(tooltip): minor tweaks

* docs(tooltip): typo

* feat(components): add Alert component (#225)

* feat(alert): add baseline implementation

* feat(alert): add styles (may be not complete)

* feat(alert): complete styles

* feat(button): add full radius styles

* feat(alert): apply styles

* feat(alert): add close button handler

* chore(alert): complete XML docs summaries

* chore(docs): adjust background colors for preview and toolbar

* chore(components): darken text color for warning flat variant

* chore(alert): styling tweaks

* feat(docs): add Alert page

* chore(alert): add missing close-button slot attribute

* chore(alert): ensure TitleContent takes precedence over Title

* docs(alert): add callout regarding Title and TitleContent parameters usage

* test(alert): add tests

* fix(theme): add tw custom CSS class-based dark mode variant (#226)

* refactor(theme): replace C# theme config with a new CSS-first approach (#229)

* feat(theme): add light and dark theme CSS files

* refactor(components): cleanup theme provider

* refactor(theme): remove Theme directory

* test(theme): remove Theme directory

* docs(theme): remove theme config

* fix(alert): correct RenderFragment parameters usage

* chore(theme): correct theme variables

* docs(customization): replace Customization section with Theming

* test(theme-provider): remove all tests

* feat(theme): introduce a mechanism to toggle light/dark modes (#230)

* feat(theme): introduce Theme service to manage and persist theme settings (JS)

* feat(theme): introduce Theme service to manage and persist theme settings

* docs(*): rename ComponentStatus enum to PageStatus

* docs(theming): add Dark Mode page

* chore(docs): component rename

* feat(button): add new `IconOnly` parameter

* docs(button): add demo for `IconOnly` parameter

* refactor(*): remove all bundled Google Material Icons and related code (#232)

* build: add new shared icons project

* feat(icons): add base icon component

* feat(icons): add dynamic icon component

* feat(icons): add some icons

* build(deps): reference icons in components

* refactor(accordion): use new icon components

* docs(components): use new icons in Callout components

* docs(*): use new icons

* refactor(icons): add "Icon" suffix; add more icons

* docs(*): use new icons

* test(*): fix tests

* refactor(components): remove `LumexIcon` component

* docs(datagrid): formatting

* refactor(icons): remove all Google Material Icons

* refactor(icons): remove script for downloading/updating icons

* feat(components): add dark mode support (#234)

* feat(alert): add dark mode support

* feat(button): add dark mode support

* feat(chip): add dark mode support

* feat(datagrid): add dark mode support

* feat(textbox/numbox): add dark mode support

* feat(listbox): add dark mode support

* feat(menu): add dark mode support

* feat(select): add dark mode support

* feat(tabs): add dark mode support

* fix(theme): ensure default values are of correct shade in dark mode

* feat(theme): add `color-scheme` in light theme

* fix(icons): use better icons for alert component

* docs: add dark mode support + theme toggle (#235)

* docs: add dark mode support

* docs: add missing border for the preview component

* docs: remove extra border in the preview code component

* chore(badge): improve flat variant contrast in light theme

* chore(tabs): remove `EditorRequired` attribute from `Id` param

* docs: add null check and theme class cleanup to prevent issues

* docs: remove IPopoverService injection from theme toggle component

* chore(components): remove unused / redundant types

* fix(data-grid): correct outside click handler creation

* feat(popover): enable position autoUpdate

* refactor(popover): make use of popover trigger component instead of service

* feat(dropdown): introduce dropdown trigger component instead of relying on popover service

* docs(dark-mode): update theme toggle dropdown example

* docs: add Home page with library usage examples (#241)

* feat(icons): add more icons

* docs: add showcases on home page

* chore(components): adjust some styles

* docs(overview): update paths

* chore(docs): nits

* fix(components): add missing popover js parts

* chore(docs): nits

* fix(popover): use overlay to close instead of custom outside click event

* fix(tooltip): remove pressed effect from on trigger hover

* chore(showcases): complete column visibility toggling in UserTable example

* chore(showcases): adjust legend color in usage chart

* test(popover): update tests

* chore(docs): coderabbit suggestions

* chore(*): migrate to DigitalOcean app platform

* fix(*): update custom LumexUI targets to copy theme files before build

* chore(*): add missing css files in the pack

* v2.0.0-preview.4

* perf(*): optimize fonts in docs app

* fix(docs): stylesheets import ordering

* perf(docs): optimize docs CSS output

* fix(docs): correct linux tailwind executable file name

* chore(*): delete update-icons.yml

* perf(docs): enable stream rendering on all pages

* fix(docs): ensure initial theme is set

* docs(overview): update content

* docs(installation): update content

* docs(design-tokens): update content

* docs(customization): update content

* docs(dark-mode): update content

* docs(accordion): update content

* refactor(docs): replace `Callout` with `LumexAlert`

* docs(avatar): update content

* docs(components): replace `Code` component usages with HTML tag

* fix(docs): ensure ThemeSelector sets correct theme value on init

* docs(landing): make adaptive

* docs(customization): add dark mode for global theme sample

* refactor(components): remove deprecated `Root` slot

* refactor(utils): remove redundant; update access modifiers

* docs(card): update slot names

* refactor(docs): make stream rendering global

* docs(header): remove active state from links; update stars counter

* ci(*): remove deploy.yml

* ci: run build-test on PR to main

* v2.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@coderabbitai coderabbitai bot mentioned this pull request Feb 8, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📦 Scope: Components Improvements or additions to components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Spinner component

1 participant