Skip to content

Commit

Permalink
feat(components): add new link component (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
desmondinho authored May 21, 2024
1 parent 62cfb48 commit bc95af4
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 2 deletions.
14 changes: 14 additions & 0 deletions src/LumexUI/Common/Enums/Underline.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 Underline
{
None,

Hover,

Always
}
3 changes: 1 addition & 2 deletions src/LumexUI/Components/Bases/LumexComponentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@ public abstract class LumexComponentBase : ComponentBase
/// 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>();
public IReadOnlyDictionary<string, object?>? AdditionalAttributes { get; set; }

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

private protected virtual string? RootClass => Class;

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

<LumexComponent As="a"
Class="@RootClass"
Style="@RootStyle"
@attributes="@Attributes">
@ChildContent
</LumexComponent>
74 changes: 74 additions & 0 deletions src/LumexUI/Components/Link/LumexLink.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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 LumexUI.Common;
using LumexUI.Styles;

using Microsoft.AspNetCore.Components;

namespace LumexUI;

public partial class LumexLink : LumexComponentBase
{
/// <summary>
/// Gets or sets content to be rendered inside the link.
/// </summary>
[Parameter] public RenderFragment? ChildContent { get; set; }

/// <summary>
/// Gets or sets a value representing the URL route to be navigated to.
/// </summary>
[Parameter] public string? Href { get; set; }

/// <summary>
/// Gets or sets a color of the link.
/// </summary>
/// <remarks>
/// Default value is <see cref="ThemeColor.Primary"/>
/// </remarks>
[Parameter] public ThemeColor Color { get; set; } = ThemeColor.Primary;

/// <summary>
/// Gets or sets the underline style for the link.
/// </summary>
/// <remarks>
/// Default value is <see cref="Underline.None"/>
/// </remarks>
[Parameter] public Underline Underline { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the link is disabled.
/// </summary>
[Parameter] public bool Disabled { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the link should open in the new tab.
/// </summary>
/// <remarks>
/// Sets target to <c>_blank</c> and rel to <c>noopener noreferrer</c>.
/// </remarks>
[Parameter] public bool External { get; set; }

private protected override string? RootClass =>
TwMerge.Merge( Link.GetStyles( this ) );

private IReadOnlyDictionary<string, object?> Attributes
{
get
{
var attributes = new Dictionary<string, object?>( AdditionalAttributes ?? new Dictionary<string, object?>() )
{
{ "href", Href }
};

if( External )
{
attributes["target"] = "_blank";
attributes["rel"] = "noopener noreferrer";
}

return attributes;
}
}
}
60 changes: 60 additions & 0 deletions src/LumexUI/Styles/Link.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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 System.Diagnostics.CodeAnalysis;

using LumexUI.Common;
using LumexUI.Utilities;

namespace LumexUI.Styles;

[ExcludeFromCodeCoverage]
internal readonly record struct Link
{
private readonly static string _base = ElementClass.Empty()
.Add( "relative" )
.Add( "inline-flex" )
.Add( "items-center" )
.Add( "hover:opacity-hover" )
.Add( "active:opacity-60" )
.Add( "transition-opacity" )
.ToString();

private readonly static string _disabled = ElementClass.Empty()
.Add( "opacity-disabled" )
.Add( "pointer-events-none" )
.ToString();

private static ElementClass GetColorStyles( ThemeColor color )
{
return ElementClass.Empty()
.Add( "text-default", when: color is ThemeColor.Default )
.Add( "text-primary", when: color is ThemeColor.Primary )
.Add( "text-secondary", when: color is ThemeColor.Secondary )
.Add( "text-success", when: color is ThemeColor.Success )
.Add( "text-warning", when: color is ThemeColor.Warning )
.Add( "text-danger", when: color is ThemeColor.Danger )
.Add( "text-info", when: color is ThemeColor.Info );
}

private static ElementClass GetUnderlineStyles( Underline underline )
{
return ElementClass.Empty()
.Add( "no-underline", when: underline is Underline.None )
.Add( "hover:underline", when: underline is Underline.Hover )
.Add( "underline", when: underline is Underline.Always )
.Add( "underline-offset-4", when: underline is not Underline.None );
}

public static string GetStyles( LumexLink link )
{
return ElementClass.Empty()
.Add( _base )
.Add( _disabled, when: link.Disabled )
.Add( GetColorStyles( link.Color ) )
.Add( GetUnderlineStyles( link.Underline ) )
.Add( link.Class )
.ToString();
}
}
38 changes: 38 additions & 0 deletions tests/LumexUI.Tests/Components/Link/LinkTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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.Extensions.DependencyInjection;

using TailwindMerge;

namespace LumexUI.Tests.Components;

public class LinkTests : TestContext
{
public LinkTests()
{
Services.AddSingleton<TwMerge>();
}

[Fact]
public void Link_ShouldRenderCorrectly()
{
var action = () => RenderComponent<LumexLink>();

action.Should().NotThrow();
}

[Fact]
public void Link_External_ShouldHaveCorrectAttributes()
{
var cut = RenderComponent<LumexLink>( p => p
.Add( p => p.External, true )
);

var link = cut.Find( "a" );

link.GetAttribute( "target" ).Should().Be( "_blank" );
link.GetAttribute( "rel" ).Should().Be( "noopener noreferrer" );
}
}

0 comments on commit bc95af4

Please sign in to comment.