Skip to content

Commit

Permalink
feat(components): add button component (#4)
Browse files Browse the repository at this point in the history
* feat(components): add the base component

* build(deps): add TailwindMerge.NET package and register in DI containers

* docs: install Tailwind CSS

* chore(components): inject TwMerge into the base component

* feat(components): add basic button implementation

* chore(utils): convert `CssBuilder` from `struct` to `record struct`

* tests(components): add basic button implementation tests

* chore(components): add RootClass and RootStyle properties to the base component

* refactor(components): transform button styles properties into fields; make button styles a readonly record struct

* chore(components): add `Color` parameter to the button component

* chore(components): add `Variant` parameter to the button component

* feat(components): add color variants constants (not populated yet)

* chore(components): update button styles to handle the variant and the color

* chore(components): exclude button styles from code coverage

* test(components): add new test for the click event; nits

* chore(compoenents): add `StartIcon` and `EndIcon` parameters to the button component

* ci(build-test): run Build and Test on Release configuration
  • Loading branch information
desmondinho authored May 15, 2024
1 parent a5231f1 commit 487e1f2
Show file tree
Hide file tree
Showing 25 changed files with 614 additions and 9 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ jobs:
run: dotnet restore

- name: Build
run: dotnet build --no-restore
run: dotnet build -c Release --no-restore

- name: Test
run: dotnet test --no-build --no-restore --verbosity normal --collect:"XPlat Code Coverage"
run: dotnet test -c Release --no-build --verbosity normal --collect:"XPlat Code Coverage"

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -399,5 +399,5 @@ FodyWeavers.xsd

# Automatically generated assets
/src/LumexUI/wwwroot/*
/src/LumexUI.Grid/wwwroot/*
/docs/LumexUI.Docs/wwwroot/css/*
/docs/LumexUI.Docs/*.exe
9 changes: 9 additions & 0 deletions LumexUI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DBC44B8-5B9
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{3F4DE3FA-2636-440C-ACCC-137BAD95BAC4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{898610F1-A6C1-422C-84C9-C1E386957F31}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LumexUI.Tests", "tests\LumexUI.Tests\LumexUI.Tests.csproj", "{06E849E9-A8EA-410B-83BC-0366524C4953}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -54,6 +58,10 @@ Global
{157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C}.Release|Any CPU.Build.0 = Release|Any CPU
{06E849E9-A8EA-410B-83BC-0366524C4953}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06E849E9-A8EA-410B-83BC-0366524C4953}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06E849E9-A8EA-410B-83BC-0366524C4953}.Release|Any CPU.ActiveCfg = Release|Any CPU
{06E849E9-A8EA-410B-83BC-0366524C4953}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -65,6 +73,7 @@ Global
{85A5E6D0-116C-4EAF-A9A8-7F074625C1F0} = {3F4DE3FA-2636-440C-ACCC-137BAD95BAC4}
{4F70D87A-4766-4249-9B74-0B63A987BB15} = {3F4DE3FA-2636-440C-ACCC-137BAD95BAC4}
{157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C} = {3F4DE3FA-2636-440C-ACCC-137BAD95BAC4}
{06E849E9-A8EA-410B-83BC-0366524C4953} = {898610F1-A6C1-422C-84C9-C1E386957F31}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1B8CD55B-BEF0-42D1-936A-BC1FB5D02CE2}
Expand Down
5 changes: 4 additions & 1 deletion docs/LumexUI.Docs.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

using TailwindMerge.Extensions;

namespace LumexUI.Docs.Client;

internal class Program
Expand All @@ -16,7 +18,8 @@ private static async Task Main( string[] args )
builder.RootComponents.Add<HeadOutlet>( "head::after" );

builder.Services.AddScoped( sp => new HttpClient { BaseAddress = new Uri( builder.HostEnvironment.BaseAddress ) } );
builder.Services.AddTailwindMerge();

await builder.Build().RunAsync();
await builder.Build().RunAsync();
}
}
1 change: 1 addition & 0 deletions docs/LumexUI.Docs.Server/Pages/_ServerLayout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="~/" />
<link href="_content/LumexUI.Docs/css/docs.min.css" rel="stylesheet" />
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions docs/LumexUI.Docs.Server/Pages/_WebAssemblyHost.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LumexUI - Blazor Components Library</title>
<base href="/webassembly/" />
<link href="_content/LumexUI.Docs/css/docs.min.css" rel="stylesheet" />
</head>

<body>
Expand Down
3 changes: 3 additions & 0 deletions docs/LumexUI.Docs.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

using TailwindMerge.Extensions;

var builder = WebApplication.CreateBuilder( args );

// Add services to the container.

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddTailwindMerge();

var app = builder.Build();

Expand Down
10 changes: 10 additions & 0 deletions docs/LumexUI.Docs/LumexUI.Docs.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,15 @@
<ProjectReference Include="..\..\src\LumexUI\LumexUI.csproj" />
<ProjectReference Include="..\..\src\LumexUI.Utilities\LumexUI.Utilities.csproj" />
</ItemGroup>

<Target Name="InstallTailwind" AfterTargets="BeforeBuild" Condition="'$(Configuration)'=='Debug' And !Exists('tailwindcss.exe')">
<Exec Command="curl -LO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-windows-x64.exe" />
<Exec Command="ren tailwindcss-windows-x64.exe tailwindcss.exe" />
</Target>

<Target Name="CompileCSS" AfterTargets="InstallTailwind" Condition=" '$(Configuration)'=='Debug' ">
<Exec Command="tailwindcss -i ./Styles/globals.css -o ./wwwroot/css/docs.css" />
<Exec Command="tailwindcss -i ./Styles/globals.css -o ./wwwroot/css/docs.min.css --minify" />
</Target>

</Project>
32 changes: 32 additions & 0 deletions docs/LumexUI.Docs/Styles/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}

#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}

.blazor-error-boundary::after {
content: "An error has occurred."
}
10 changes: 10 additions & 0 deletions docs/LumexUI.Docs/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"../../src/LumexUI/Styles/*.cs"
],
theme: {
extend: {},
},
plugins: [],
}
8 changes: 3 additions & 5 deletions src/LumexUI.Utilities/Builders/CssBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace LumexUI.Utilities;

public struct CssBuilder
public record struct CssBuilder
{
private string? stringBuffer;
private string prefix;
Expand Down Expand Up @@ -129,12 +129,10 @@ public CssBuilder AddClassFromAttributes( IReadOnlyDictionary<string, object> ad
/// Finalize the completed CSS Classes as a string.
/// </summary>
/// <returns>string</returns>
public string Build()
public readonly string Build()
{
// String buffer finalization code
return stringBuffer != null ? stringBuffer.Trim() : string.Empty;
}

// ToString should only and always call Build to finalize the rendered string.
public override string ToString() => Build();
public readonly override string ToString() => Build();
}
14 changes: 14 additions & 0 deletions src/LumexUI/Common/Enums/ButtonType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) LumexUI 2024
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

namespace LumexUI.Common;

public enum ButtonType
{
Button,

Submit,

Reset
}
14 changes: 14 additions & 0 deletions src/LumexUI/Common/Enums/Size.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) LumexUI 2024
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

namespace LumexUI.Common;

public enum Size
{
Small,

Medium,

Large
}
22 changes: 22 additions & 0 deletions src/LumexUI/Common/Enums/ThemeColor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) LumexUI 2024
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

namespace LumexUI.Common;

public enum ThemeColor
{
Default,

Primary,

Secondary,

Success,

Warning,

Danger,

Info
}
20 changes: 20 additions & 0 deletions src/LumexUI/Common/Enums/Variant.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) LumexUI 2024
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

namespace LumexUI.Common;

public enum Variant
{
Solid,

Outlined,

Flat,

Shadow,

Ghost,

Light
}
39 changes: 39 additions & 0 deletions src/LumexUI/Components/Bases/LumexComponentBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) LumexUI 2024
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

using Microsoft.AspNetCore.Components;

using TailwindMerge;

namespace LumexUI;

public abstract class LumexComponentBase : ComponentBase
{
/// <summary>
/// Gets or sets an HTML tag of the component.
/// </summary>
[Parameter] public string As { get; set; } = "div";

/// <summary>
/// Gets or sets CSS class names that will be applied to the component.
/// </summary>
[Parameter] public string? Class { get; set; }

/// <summary>
/// Gets or sets styles that will be applied to the component.
/// </summary>
[Parameter] public string? Style { get; set; }

/// <summary>
/// Gets or sets a collection of additional attributes that will be applied to the component.
/// </summary>
[Parameter( CaptureUnmatchedValues = true )]
public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; } = new Dictionary<string, object>();

[Inject] protected TwMerge TwMerge { get; set; } = default!;

private protected virtual string? RootClass => Class;

private protected virtual string? RootStyle => Style;
}
24 changes: 24 additions & 0 deletions src/LumexUI/Components/Button/LumexButton.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@namespace LumexUI
@inherits LumexComponentBase

<LumexComponent As="button"
Class="@RootClass"
Style="@RootStyle"
type="@Type.ToDescription()"
disabled="@Disabled"
@attributes="@AdditionalAttributes"
@onclick="@OnClickAsync">
@if( !string.IsNullOrWhiteSpace( StartIcon ) )
{
// TODO: Replace with the real icon component
<span>@StartIcon</span>
}

@ChildContent

@if( !string.IsNullOrWhiteSpace( EndIcon ) )
{
// TODO: Replace with the real icon component
<span>@EndIcon</span>
}
</LumexComponent>
Loading

0 comments on commit 487e1f2

Please sign in to comment.