Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FluentTooltip] Add the HideTooltipOnCursorLeave property #1571

Merged
merged 5 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7479,6 +7479,13 @@
Gets the default tooltip options.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentTooltip.HideTooltipOnCursorLeave">
<summary>
Gets or sets the value indicating whether the library should close the tooltip if the cursor leaves the anchor and the tooltip.
By default, the tooltip closes if the cursor leaves the anchor, but not the tooltip itself.
You can configure this behavior globally using the <see cref="P:Microsoft.FluentUI.AspNetCore.Components.LibraryConfiguration.HideTooltipOnCursorLeave"/> property.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentTooltip.UseTooltipService">
<summary>
Use ITooltipService to create the tooltip, if this service was injected.
Expand Down Expand Up @@ -12678,6 +12685,17 @@
If set to true, add the FluentTooltipProvider component at end of the MainLayout.razor page.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.LibraryConfiguration.RequiredLabel">
<summary>
Gets or sets the required label for the form fields.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.LibraryConfiguration.HideTooltipOnCursorLeave">
<summary>
Gets or sets the value indicating whether the library should close the tooltip if the cursor leaves the anchor and the tooltip.
By default, the tooltip closes if the cursor leaves the anchor, but not the tooltip itself.
</summary>
</member>
<member name="T:Microsoft.FluentUI.AspNetCore.Components.Resources.TimeAgoResource">
<summary>
A strongly-typed resource class, for looking up localized strings, etc.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<FluentStack>
<FluentButton Id="delay1" Appearance=Appearance.Accent>Tooltip at the start</FluentButton>
<FluentTooltip Anchor="delay1" Position=TooltipPosition.Start Delay=100>I'm helping (very quick)!</FluentTooltip>
<FluentTooltip Anchor="delay1" HideTooltipOnCursorLeave="true" Position=TooltipPosition.Start Delay=100>I'm helping (very quick)!</FluentTooltip>

<FluentButton Id="delay2" Appearance=Appearance.Accent>Tooltip at the top</FluentButton>
<FluentTooltip Anchor="delay2" Position=TooltipPosition.Top Delay=200>I'm helping (quick)!</FluentTooltip>
<FluentTooltip Anchor="delay2" HideTooltipOnCursorLeave="true" Position=TooltipPosition.Top Delay=200>I'm helping (quick)!</FluentTooltip>

<FluentButton Id="delay3" Appearance=Appearance.Accent>Tooltip at the end</FluentButton>
<FluentTooltip Anchor="delay3" Position=TooltipPosition.End Delay=600>I'm helping (slow)!</FluentTooltip>
<FluentTooltip Anchor="delay3" HideTooltipOnCursorLeave="true" Position=TooltipPosition.End Delay=600>I'm helping (slow)!</FluentTooltip>
</FluentStack>
8 changes: 6 additions & 2 deletions examples/Demo/Shared/Pages/Tooltip/TooltipPage.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@page "/Tooltip"
@page "/Tooltip"

@using FluentUI.Demo.Shared.Pages.Tooltip.Examples

Expand Down Expand Up @@ -56,7 +56,11 @@ and will be written at the end of the HTML page to support the different z-index
</DemoSection>

<DemoSection Title="Different delays" Component="@typeof(TooltipDelay)">
<Description>Hover one of the buttons to have a tooltip appear on hover at the position mentioned.</Description>
<Description>
Hover one of the buttons to have a tooltip appear on hover at the position mentioned. <br/>
Unlike the other tooltip examples, these contain the <code>HideTooltipOnCursorLeave</code> attribute.
which hides the tooltip when the cursor leaves (i.e. not hovering) its anchor (even when the cursor hovers the tooltip itself).
</Description>
</DemoSection>

<DemoSection Title="Always visible" Component="@typeof(TooltipVisible)" />
Expand Down
28 changes: 28 additions & 0 deletions src/Core/Components/Tooltip/FluentTooltip.razor.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.FluentUI.AspNetCore.Components.Components.Tooltip;
using Microsoft.JSInterop;

namespace Microsoft.FluentUI.AspNetCore.Components;

public partial class FluentTooltip : FluentComponentBase, IDisposable
{
private readonly Guid _guid = Guid.NewGuid();
private ITooltipService? _tooltipService = null;
private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Tooltip/FluentTooltip.razor.js";

private IJSObjectReference? JSModule { get; set; }

[Inject]
private IJSRuntime JSRuntime { get; set; } = default!;

[Inject]
private LibraryConfiguration? LibraryConfiguration { get; set; }

/// <summary>
/// Gets or sets a reference to the list of registered services.
Expand All @@ -28,6 +38,14 @@ public partial class FluentTooltip : FluentComponentBase, IDisposable
/// </summary>
protected virtual TooltipGlobalOptions? GlobalOptions => TooltipService?.GlobalOptions;

/// <summary>
/// Gets or sets the value indicating whether the library should close the tooltip if the cursor leaves the anchor and the tooltip.
/// By default, the tooltip closes if the cursor leaves the anchor, but not the tooltip itself.
/// You can configure this behavior globally using the <see cref="LibraryConfiguration.HideTooltipOnCursorLeave"/> property.
/// </summary>
[Parameter]
public bool? HideTooltipOnCursorLeave { get; set; }

/// <summary>
/// Use ITooltipService to create the tooltip, if this service was injected.
/// If the <see cref="ChildContent"/> is dynamic, set this to false.
Expand Down Expand Up @@ -117,6 +135,7 @@ private void HandleDismissed()
/// <summary />
protected override void OnInitialized()
{
HideTooltipOnCursorLeave = HideTooltipOnCursorLeave ?? LibraryConfiguration?.HideTooltipOnCursorLeave;
_tooltipService = ServiceProvider?.GetService<ITooltipService>();

if (TooltipService != null && UseTooltipService)
Expand All @@ -135,6 +154,15 @@ protected override void OnInitialized()
}
}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && !string.IsNullOrEmpty(Anchor) && HideTooltipOnCursorLeave == true)
{
JSModule ??= await JSRuntime.InvokeAsync<IJSObjectReference>("import", JAVASCRIPT_FILE);
await JSModule.InvokeVoidAsync("tooltipHideOnCursorLeave", Anchor);
}
}

/// <summary />
public void Dispose()
{
Expand Down
21 changes: 21 additions & 0 deletions src/Core/Components/Tooltip/FluentTooltip.razor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export function tooltipHideOnCursorLeave(anchorId) {
const srcElement = document.getElementById(anchorId);
const tooltipSelector = `fluent-tooltip[anchor="${anchorId}"]:not([visible])`;

if (!!srcElement) {
srcElement.addEventListener("mouseleave", function (event) {
const tooltip = document.querySelector(tooltipSelector);
if (!!tooltip) {
tooltip.style.display = "none";
}
});

srcElement.addEventListener("mouseenter", function (event) {
const tooltip = document.querySelector(tooltipSelector);
if (!!tooltip) {
tooltip.style.display = null;
}
});

}
}
9 changes: 9 additions & 0 deletions src/Core/Infrastructure/LibraryConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,20 @@ public class LibraryConfiguration
/// </summary>
public bool UseTooltipServiceProvider { get; set; } = true;

/// <summary>
/// Gets or sets the required label for the form fields.
/// </summary>
public MarkupString RequiredLabel { get; set; } = (MarkupString)
"""
<span aria-label="required" aria-hidden="true" style="padding-inline-start: calc(var(--design-unit) * 1px); color: var(--error); pointer-events: none;">*</span>
""";

/// <summary>
/// Gets or sets the value indicating whether the library should close the tooltip if the cursor leaves the anchor and the tooltip.
/// By default, the tooltip closes if the cursor leaves the anchor, but not the tooltip itself.
/// </summary>
public bool HideTooltipOnCursorLeave { get; set; } = false;

public LibraryConfiguration()
{
}
Expand Down
10 changes: 10 additions & 0 deletions tests/Core/Tooltip/FluentTooltipTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Microsoft.FluentUI.AspNetCore.Components.Tests.Tooltip;

public class FluentTooltipTests : TestBase
{
[Inject]
private LibraryConfiguration LibraryConfiguration { get; set; } = new LibraryConfiguration();

public FluentTooltipTests()
{
TestContext.Services.AddSingleton(LibraryConfiguration);
}

[Fact]
public void FluentTooltip_Default()
{
Expand Down
Loading