Skip to content
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
73 changes: 73 additions & 0 deletions src/Core.Scripts/src/FluentUIWebComponentsOverride.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

/// <reference path="./d-ts/Blazor.d.ts" />

export namespace Microsoft.FluentUI.Override {
interface NavigationElement extends HTMLElement {
href: string;
internalProxyAnchor: { click(): void };
handleNavigation(newTab: boolean): void;
}

/**
* Override the default behavior of the Fluent UI Web Components to use Blazor's features.
*/
export function overrideComponents() {
waitForCustomElements().then(async registry => {

/**
* fluent-anchor-button
*/
const anchorButton = await registry.whenDefined('fluent-anchor-button');
anchorButton.prototype.handleNavigation = handleNavigation;

/**
* fluent-link
*/
const link = await registry.whenDefined('fluent-link');
link.prototype.handleNavigation = handleNavigation;
});
}

/*
Original code: https://github.com/microsoft/fluentui/blob/master/packages/web-components/src/anchor-button/anchor-button.base.ts

private handleNavigation(newTab: boolean): void {
newTab ? window.open(this.href, '_blank') : this.internalProxyAnchor.click();
}
*/
function handleNavigation(this: NavigationElement, newTab: boolean): void {
if (newTab) {
window.open(this.href, '_blank');
return;
}
Comment thread
dvoituron marked this conversation as resolved.

const url = new URL(this.href, document.baseURI);
if (url.origin === location.origin) {
const forceLoad = this.getAttribute('force-load') === 'true';
Blazor.navigateTo(url.pathname + url.search + url.hash, forceLoad);
}
else {
this.internalProxyAnchor.click();
}
}

/**
* Wait for the customElements registry to be available before overriding the components.
* This is necessary because the Fluent UI Web Components are defined asynchronously.
* @returns A promise that resolves with the customElements registry.
*/
function waitForCustomElements(): Promise<CustomElementRegistry> {
return new Promise(resolve => {
const checkCustomElements = () => {
if (typeof customElements !== 'undefined') {
resolve(customElements);
return;
}

setTimeout(checkCustomElements, 10);
};

checkCustomElements();
});
}
}
5 changes: 5 additions & 0 deletions src/Core.Scripts/src/Startup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Microsoft as LoggerFile } from './Utilities/Logger';
import { Microsoft as ThemeFile } from './Utilities/Theme';
import { Microsoft as FluentUIComponentsFile } from './FluentUIWebComponents';
import { Microsoft as FluentUIWebComponentsOverrideFile } from './FluentUIWebComponentsOverride';
import { Microsoft as FluentPageScriptFile } from './Components/PageScript/FluentPageScript';
import { Microsoft as FluentPopoverFile } from './Components/Popover/FluentPopover';
import { Microsoft as FluentOverlayFile } from './Components/Overlay/FluentOverlay';
Expand All @@ -16,6 +17,7 @@ export namespace Microsoft.FluentUI.Blazor.Startup {
// Alias
import Logger = LoggerFile.FluentUI.Blazor.Utilities.Logger;
import FluentUIComponents = FluentUIComponentsFile.FluentUI.Blazor.FluentUIWebComponents;
import FluentUIOverride = FluentUIWebComponentsOverrideFile.FluentUI.Override;
import FluentPageScript = FluentPageScriptFile.FluentUI.Blazor.Components.PageScript;
import FluentPopover = FluentPopoverFile.FluentUI.Blazor.Components.Popover;
import FluentOverlay = FluentOverlayFile.FluentUI.Blazor.Components.Overlay;
Expand All @@ -42,6 +44,9 @@ export namespace Microsoft.FluentUI.Blazor.Startup {
// Define Fluent UI components
FluentUIComponents.defineComponents();

// Override Fluent UI components to use Blazor's features
FluentUIOverride.overrideComponents();

// Finishing
beforeStartCalled = true;
}
Expand Down
4 changes: 4 additions & 0 deletions src/Core.Scripts/src/d-ts/Blazor.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ interface ThemeSettings {
interface Blazor {
addEventListener?: (name: string, callback: (event: any) => void) => void;

navigateTo: (uri: string, options: NavigationOptions | boolean) => void;

registerCustomEventType(eventName: string, options: EventTypeOptions): void;

theme: {
Expand Down Expand Up @@ -41,3 +43,5 @@ interface Blazor {
switchDirection(): void,
}
}

declare const Blazor: Blazor;
1 change: 1 addition & 0 deletions src/Core/Components/Button/FluentAnchorButton.razor
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
name="@Name"
role="@(string.IsNullOrEmpty(Title) ? null : "link")"
aria-label="@Title"
force-load="@(ForceLoad ? "true" : null)"
@attributes="@AdditionalAttributes">
@if (IconStart != null)
{
Expand Down
7 changes: 7 additions & 0 deletions src/Core/Components/Button/FluentAnchorButton.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ public FluentAnchorButton(LibraryConfiguration configuration) : base(configurati
[Parameter]
public string? Tooltip { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the navigation should force a full page load instead of using Blazor's built-in navigation.
/// Default is <see langword="false" />, which uses Blazor's navigation.
/// </summary>
[Parameter]
public bool ForceLoad { get; set; }

/// <summary />
protected override async Task OnInitializedAsync()
{
Expand Down
1 change: 1 addition & 0 deletions src/Core/Components/Link/FluentLink.razor
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
class="@ClassValue"
style="@StyleValue"
clickable="@OnClick.HasDelegate"
force-load="@(ForceLoad ? "true" : null)"
@onclick="@OnClickHandlerAsync"
@attributes="AdditionalAttributes">
@if (IconStart is not null)
Expand Down
7 changes: 7 additions & 0 deletions src/Core/Components/Link/FluentLink.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ public FluentLink(LibraryConfiguration configuration) : base(configuration) { }
[Parameter]
public string? Tooltip { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the navigation should force a full page load instead of using Blazor's built-in navigation.
/// Default is <see langword="false" />, which uses Blazor's navigation.
/// </summary>
[Parameter]
public bool ForceLoad { get; set; }

/// <summary />
protected override async Task OnInitializedAsync()
{
Expand Down
13 changes: 13 additions & 0 deletions tests/Core/Components/Button/FluentAnchorButtonTests.razor
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@
cut.Verify();
}

[Theory]
[InlineData(false, null)]
[InlineData(true, "true")]
public void FluentAnchorButton_ForceLoadAttribute(bool forceLoad, string? expectedValue)
{
// Arrange && Act
var cut = Render(@<FluentAnchorButton ForceLoad="@forceLoad">fluent-anchor-button</FluentAnchorButton>);

// Assert
var attribute = cut.Find("fluent-anchor-button").GetAttribute("force-load");
Assert.Equal(expectedValue, attribute);
}

[Theory]
[InlineData(LinkTarget.Blank, "_blank")]
[InlineData(LinkTarget.Parent, "_parent")]
Expand Down
13 changes: 13 additions & 0 deletions tests/Core/Components/Link/FluentLinkTests.razor
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@
cut.Verify();
}

[Theory]
[InlineData(false, null)]
[InlineData(true, "true")]
public void FluentLink_ForceLoadAttribute(bool forceLoad, string? expectedValue)
{
// Arrange && Act
var cut = Render(@<FluentLink ForceLoad="@forceLoad">fluent-link</FluentLink>);

// Assert
var attribute = cut.Find("fluent-link").GetAttribute("force-load");
Assert.Equal(expectedValue, attribute);
}

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