From 1e581d0ccca61fb10caf4f23de327ed12962de48 Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Wed, 10 Jul 2024 10:54:42 +0200 Subject: [PATCH 01/22] Prep for next version --- Directory.Build.props | 4 ++-- eng/pipelines/version.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 77b78d64e9..9953c76510 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,8 +12,8 @@ $(MSBuildThisFileDirectory) true - 4.9.1 - 4.9.1 + 4.9.2 + 4.9.2 $(VersionFile) $(VersionFile) diff --git a/eng/pipelines/version.yml b/eng/pipelines/version.yml index d29cb40a35..7b966e667d 100644 --- a/eng/pipelines/version.yml +++ b/eng/pipelines/version.yml @@ -2,5 +2,5 @@ variables: # File and Package version # dev branch: 1.2.4-Preview-23282-1' (PackageSuffix is always ignored in Dev branch) # main branch: 1.2.4-RC.1' (PackageSuffix is ignored, if empty, in Main branch) - FileVersion: '4.9.1' # Set the next final version here. + FileVersion: '4.9.2' # Set the next final version here. PackageSuffix: '' From 1ebdde86869c0ceb27619020ff63452c2e1072c2 Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Thu, 11 Jul 2024 15:27:23 +0200 Subject: [PATCH 02/22] [Docs] Better column distribution for homepage (#2360) - Better column distibution for homepage --- README.md | 2 +- examples/Demo/Shared/Pages/Home/Home.razor | 28 +++++++++++++------ .../Demo/Shared/Shared/DemoMainLayout.razor | 2 +- examples/Demo/Shared/wwwroot/css/site.css | 9 ++++-- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ddcbd07d33..49bf1cb0ea 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ## Introduction -The `Microsoft.FluentUI.AspNetCore.Components` package provides a set of [Blazor](https://blazor.net) components which are used to build applications that have a Fluent design (i.e. have the look and feel or modern Microsoft applications). +The `Microsoft.FluentUI.AspNetCore.Components` package provides a set of [Blazor](https://blazor.net) components which are used to build applications that have a Fluent design (i.e. have the look and feel of modern Microsoft applications). Some of the components in the library are wrappers around Microsoft's official Fluent UI Web Components. Others are components that leverage the Fluent Design System or make it easier to work with Fluent UI. To get up and running with the library, see the **Getting Started** section below. diff --git a/examples/Demo/Shared/Pages/Home/Home.razor b/examples/Demo/Shared/Pages/Home/Home.razor index 90d3a370e0..3af1cc9914 100644 --- a/examples/Demo/Shared/Pages/Home/Home.razor +++ b/examples/Demo/Shared/Pages/Home/Home.razor @@ -6,7 +6,7 @@ - +

Welcome!

The Fluent UI Blazor library provides a robust and extensive set of Blazor components. Some of those @@ -25,7 +25,7 @@

- +

This is the demo and documentation site for version @_version of the library. This version supports .NET 8 only


@@ -40,8 +40,20 @@ If you are already up-and-running and upgrading from an earlier version of the library, please go to the What's new page for information on additions, fixes and (breaking) changes.

+

Components & render modes

+

+ As described in the Blazor documentation: +

+
+ By default, components use Static Server-side rendering (SSR). The component renders to the response stream and there is no interactivity. +
+

+ A component inherits its render mode from its parent. So unless a render mode is specified on the app, page or component level, every + component (including ours) is statically rendered on the server and will not be interactive. For the + Fluent UI Blazor library this means most components will display correctly but will not offer complete, if any, functionality. +

- +

Getting Started

By far rhe easiest way to get started is by using our templates. Setting them up is quick and easy. See the templates page @@ -60,7 +72,7 @@

- +

Scripts

As mentioned, we wrap the Fluent UI Web Components for some of our compnents. The needed script is included in the library and will @@ -86,7 +98,7 @@

- +

Using the Fluent UI Blazor components

With the package installed, you can begin using the Fluent UI Blazor components in the same way as any other Blazor component. Just be sure to add the following using statement to your views:

@@using Microsoft.FluentUI.AspNetCore.Components @@ -114,7 +126,7 @@
- +

Configuring the Design System

The Fluent UI Blazor components are built on FAST's Adaptive UI technology, which enables design customization and personalization, while @@ -127,7 +139,7 @@ - +

Support

The Microsoft Fluent UI Blazor library is an open-source project. The source code (including this demo site) is hosted in the fluentui-blazor @@ -141,7 +153,7 @@

- +

Joining the Community

Looking to get answers to questions or engage with us in real-time? diff --git a/examples/Demo/Shared/Shared/DemoMainLayout.razor b/examples/Demo/Shared/Shared/DemoMainLayout.razor index fa8da51d8f..20c7a3520e 100644 --- a/examples/Demo/Shared/Shared/DemoMainLayout.razor +++ b/examples/Demo/Shared/Shared/DemoMainLayout.razor @@ -36,7 +36,7 @@

diff --git a/examples/Demo/Shared/wwwroot/css/site.css b/examples/Demo/Shared/wwwroot/css/site.css index 54e62183b6..633043e1f3 100644 --- a/examples/Demo/Shared/wwwroot/css/site.css +++ b/examples/Demo/Shared/wwwroot/css/site.css @@ -399,14 +399,19 @@ kbd { } } -@media (min-width: 768px) and (max-width: 1024px) { +@media (min-width: 768px) and (max-width: 1279px) { fluent-select::part(control) { width: 150px; } + .content { + flex-direction: column; + } aside { padding: 1.5em 0.75em 1em 0.75em; - width: 12rem; + width: 100%; + height: 100%!important; + max-height: 100% !important; } article { From 310eeddc8fe733938826c04e12d69d4075f4525a Mon Sep 17 00:00:00 2001 From: Ehsan Vali Date: Sun, 14 Jul 2024 11:20:19 +0330 Subject: [PATCH 03/22] [DatePicker] Fix default position for RTL layout (#2372) * add 'PickerMonthChanged' event and 'DaysTemplate' property to date picker * add xml document for properties * change type of DaysTemplate of FluentDatePicker to nullable * fix default position of anchored region on datepicker for RTL layout --- src/Core/Components/DateTime/FluentDatePicker.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Components/DateTime/FluentDatePicker.razor b/src/Core/Components/DateTime/FluentDatePicker.razor index 5a4991d6b6..6c660da8b9 100644 --- a/src/Core/Components/DateTime/FluentDatePicker.razor +++ b/src/Core/Components/DateTime/FluentDatePicker.razor @@ -24,7 +24,7 @@ { Date: Mon, 15 Jul 2024 10:43:54 +0200 Subject: [PATCH 04/22] [MenuButton] Fix icon size (#2374) * Fix icon size * Fix test --------- Co-authored-by: Vincent Baaij --- .../Components/MenuButton/FluentMenuButton.razor | 2 +- ...tonTests.FluentMenuButton_Default.verified.html | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Core/Components/MenuButton/FluentMenuButton.razor b/src/Core/Components/MenuButton/FluentMenuButton.razor index 9bd9d809fc..41fb61ebf6 100644 --- a/src/Core/Components/MenuButton/FluentMenuButton.razor +++ b/src/Core/Components/MenuButton/FluentMenuButton.razor @@ -5,7 +5,7 @@
@Text - + diff --git a/tests/Core/_ToDo/MenuButton/FluentMenuButtonTests.FluentMenuButton_Default.verified.html b/tests/Core/_ToDo/MenuButton/FluentMenuButtonTests.FluentMenuButton_Default.verified.html index 9442cb4809..2c2b5838ad 100644 --- a/tests/Core/_ToDo/MenuButton/FluentMenuButtonTests.FluentMenuButton_Default.verified.html +++ b/tests/Core/_ToDo/MenuButton/FluentMenuButtonTests.FluentMenuButton_Default.verified.html @@ -1,8 +1,8 @@ -
- - - -
\ No newline at end of file +
+ + + +
From 0854021dc7f670c0bbe038355a59a73d978e4b06 Mon Sep 17 00:00:00 2001 From: Denis Voituron Date: Mon, 15 Jul 2024 16:08:30 +0200 Subject: [PATCH 05/22] Add RTL direction (#2380) --- .../Components/Splitter/FluentMultiSplitter.razor.css | 8 ++++++++ src/Core/Components/Splitter/FluentMultiSplitter.razor.js | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Core/Components/Splitter/FluentMultiSplitter.razor.css b/src/Core/Components/Splitter/FluentMultiSplitter.razor.css index e9eb196973..a0deed969a 100644 --- a/src/Core/Components/Splitter/FluentMultiSplitter.razor.css +++ b/src/Core/Components/Splitter/FluentMultiSplitter.razor.css @@ -106,6 +106,10 @@ content: '\025C0'; } + [dir="rtl"] .fluent-multi-splitter[orientation="horizontal"] ::deep > .fluent-multi-splitter-bar > span[part="collapse"]:before { + content: '\025B6'; + } + .fluent-multi-splitter[orientation="horizontal"] ::deep > .fluent-multi-splitter-bar > span[part="resize"] { height: 16px; margin: 2px 0; @@ -115,6 +119,10 @@ content: '\025B6'; } + [dir="rtl"] .fluent-multi-splitter[orientation="horizontal"] ::deep > .fluent-multi-splitter-bar > span[part="expand"]:before { + content: '\025C0'; + } + .fluent-multi-splitter[orientation="horizontal"] ::deep > .fluent-multi-splitter-bar[status="resizable"]:hover { cursor: ew-resize; } diff --git a/src/Core/Components/Splitter/FluentMultiSplitter.razor.js b/src/Core/Components/Splitter/FluentMultiSplitter.razor.js index 44858cf7e1..54f69b814f 100644 --- a/src/Core/Components/Splitter/FluentMultiSplitter.razor.js +++ b/src/Core/Components/Splitter/FluentMultiSplitter.razor.js @@ -101,13 +101,15 @@ export function startSplitterResize( mouseMoveHandler: function (e) { if (document.splitterData[el]) { + const rtl = window.getComputedStyle(pane)?.getPropertyValue('direction') === 'rtl' ? -1 : 1; document.splitterData[el].moved = true; var spacePerc = document.splitterData[el].panePerc + document.splitterData[el].paneNextPerc; var spaceLength = document.splitterData[el].paneLength + document.splitterData[el].paneNextLength; - var length = (document.splitterData[el].paneLength - - (document.splitterData[el].clientPos - (isHOrientation ? e.clientX : e.clientY))); + var length = isHOrientation + ? document.splitterData[el].paneLength - (document.splitterData[el].clientPos - e.clientX) * rtl + : document.splitterData[el].paneLength - (document.splitterData[el].clientPos - e.clientY); if (length > spaceLength) length = spaceLength; From d07f8eb803c1ffd7dacf130eacacdae02ecc0efe Mon Sep 17 00:00:00 2001 From: Denis Voituron Date: Mon, 15 Jul 2024 18:32:26 +0200 Subject: [PATCH 06/22] [Wizard] Add GoToStepAsync (#2383) --- ...crosoft.FluentUI.AspNetCore.Components.xml | 9 +++++++ .../Wizard/Examples/WizardCustomized.razor | 25 ++++++++++++------- .../Components/Wizard/FluentWizard.razor.cs | 15 ++++++++++- .../Wizard/FluentWizardStep.razor.cs | 2 +- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml index 9cbb57147e..3ca6c314d6 100644 --- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml +++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml @@ -9469,6 +9469,7 @@ This configuration overrides the whole rendering of the bottom-right section of the Wizard, including the built-in buttons and thus provides a full control over it. Custom Wizard buttons do not trigger the component OnChange and OnFinish events. + The OnChange event can be triggered using the method from your code. @@ -9503,6 +9504,14 @@ + + + Navigate to the specified step, with or without validate the current EditContexts. + + Index number of the step to display + Validate the EditContext. Default is false. + + diff --git a/examples/Demo/Shared/Pages/Wizard/Examples/WizardCustomized.razor b/examples/Demo/Shared/Pages/Wizard/Examples/WizardCustomized.razor index 49736a69a1..0e5b1790a9 100644 --- a/examples/Demo/Shared/Pages/Wizard/Examples/WizardCustomized.razor +++ b/examples/Demo/Shared/Pages/Wizard/Examples/WizardCustomized.razor @@ -11,12 +11,13 @@ } - - +
Intro @@ -29,7 +30,7 @@ ut blandit dui ullamcorper faucibus. Interdum et malesuada fames ac ante ipsum. - +
Get Started @@ -40,7 +41,7 @@ Fusce vel porta ex, imperdiet molestie nisl. Vestibulum eu ultricies mauris, eget aliquam quam. - +
Set budget @@ -52,7 +53,7 @@ turpis, eget molestie ipsum. - +
Summary @@ -74,16 +75,16 @@
@if (index > 0) { - Go to first page - Previous + Go to first page + Previous }
@if (index != lastStepIndex) { - Next - Go to last page + Next + Go to last page }
} @@ -92,5 +93,11 @@ @code { + FluentWizard MyWizard = default!; int Value = 0; + + void OnStepChange(FluentWizardStepChangeEventArgs e) + { + DemoLogger.WriteLine($"Go to step {e.TargetLabel} (#{e.TargetIndex})"); + } } diff --git a/src/Core/Components/Wizard/FluentWizard.razor.cs b/src/Core/Components/Wizard/FluentWizard.razor.cs index 460f2edcd7..dc66be79c5 100644 --- a/src/Core/Components/Wizard/FluentWizard.razor.cs +++ b/src/Core/Components/Wizard/FluentWizard.razor.cs @@ -118,6 +118,7 @@ public int Value /// This configuration overrides the whole rendering of the bottom-right section of the Wizard, /// including the built-in buttons and thus provides a full control over it. /// Custom Wizard buttons do not trigger the component OnChange and OnFinish events. + /// The OnChange event can be triggered using the method from your code. ///
[Parameter] public RenderFragment? ButtonTemplate { get; set; } @@ -248,7 +249,19 @@ protected virtual async Task OnFinishHandlerAsync(MouseEventArgs e) } } - internal async Task GoToStepAsync(int targetIndex, bool validateEditContexts) + /// + /// Navigate to the specified step, with or without validate the current EditContexts. + /// + /// Index number of the step to display + /// Validate the EditContext. Default is false. + /// + public Task GoToStepAsync(int step, bool validateEditContexts = false) + { + Value = step; + return ValidateAndGoToStepAsync(step, validateEditContexts); + } + + internal async Task ValidateAndGoToStepAsync(int targetIndex, bool validateEditContexts) { var stepChangeArgs = await OnStepChangeHandlerAsync(targetIndex, validateEditContexts); var isCanceled = stepChangeArgs?.IsCancelled ?? false; diff --git a/src/Core/Components/Wizard/FluentWizardStep.razor.cs b/src/Core/Components/Wizard/FluentWizardStep.razor.cs index 94339bfbb7..2b0bb4af67 100644 --- a/src/Core/Components/Wizard/FluentWizardStep.razor.cs +++ b/src/Core/Components/Wizard/FluentWizardStep.razor.cs @@ -192,7 +192,7 @@ private async Task OnClickHandlerAsync() return; } - await FluentWizard.GoToStepAsync(Index, validateEditContexts: Index > FluentWizard.Value); + await FluentWizard.ValidateAndGoToStepAsync(Index, validateEditContexts: Index > FluentWizard.Value); } private bool IsStepClickable From 21981af7fbeb0c3ef868a4102bc492d9e36addeb Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Tue, 16 Jul 2024 10:33:23 +0200 Subject: [PATCH 07/22] Fix #2382 by letting value follow newly selected option (#2384) --- src/Core/Components/List/ListComponentBase.razor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Core/Components/List/ListComponentBase.razor.cs b/src/Core/Components/List/ListComponentBase.razor.cs index dd998eb04d..993739c062 100644 --- a/src/Core/Components/List/ListComponentBase.razor.cs +++ b/src/Core/Components/List/ListComponentBase.razor.cs @@ -217,6 +217,8 @@ public override async Task SetParametersAsync(ParameterView parameters) if (Items.Contains(newSelectedOption)) { _currentSelectedOption = newSelectedOption; + // Make value follow new selected option + Value = GetOptionValue(_currentSelectedOption); } else { From f8998c730333eff6eb4d264bd474e017da5f4125 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 16 Jul 2024 03:42:44 -0500 Subject: [PATCH 08/22] Fix validation when @bind-Value is set for FluentWizard component (#2364) * Fixing issue where FluentWizard stops validating when @bind-Value is set fix: validation stops working when @bind-Value is set on the FluentWizard component to an integer property on the page Adding test to validate that index does not change on next within wizard component * Making recommended changes --------- Co-authored-by: ErikJohnsonLRS Co-authored-by: Denis Voituron --- src/Core/Components/Wizard/FluentWizard.razor.cs | 8 +++++--- tests/Core/Wizard/FluentWizardTests.razor | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Core/Components/Wizard/FluentWizard.razor.cs b/src/Core/Components/Wizard/FluentWizard.razor.cs index dc66be79c5..9884139cde 100644 --- a/src/Core/Components/Wizard/FluentWizard.razor.cs +++ b/src/Core/Components/Wizard/FluentWizard.razor.cs @@ -161,6 +161,7 @@ protected virtual async Task OnNextHandlerAsync(MouseEventArgs e) if (!isCanceled) { Value = targetIndex; + await ValueChanged.InvokeAsync(targetIndex); StateHasChanged(); } } @@ -183,6 +184,7 @@ protected virtual async Task OnPreviousHandlerAsync(MouseEventArgs e) if (!isCanceled) { Value = targetIndex; + await ValueChanged.InvokeAsync(targetIndex); StateHasChanged(); } } @@ -190,7 +192,8 @@ protected virtual async Task OnPreviousHandlerAsync(MouseEventArgs e) /// protected virtual async Task OnStepChangeHandlerAsync(int targetIndex, bool validateEditContexts) { - var stepChangeArgs = new FluentWizardStepChangeEventArgs(targetIndex, _steps[targetIndex].Label); + var stepChangeArgs = new FluentWizardStepChangeEventArgs(targetIndex, _steps[targetIndex].Label); + if (validateEditContexts) { var allEditContextsAreValid = _steps[Value].ValidateEditContexts(); @@ -209,8 +212,6 @@ protected virtual async Task OnStepChangeHandle await _steps[Value].InvokeOnSubmitForEditFormsAsync(); } - await ValueChanged.InvokeAsync(targetIndex); - return await OnStepChangeHandlerAsync(stepChangeArgs); } @@ -269,6 +270,7 @@ internal async Task ValidateAndGoToStepAsync(int targetIndex, bool validateEditC if (!isCanceled) { Value = targetIndex; + await ValueChanged.InvokeAsync(targetIndex); StateHasChanged(); } } diff --git a/tests/Core/Wizard/FluentWizardTests.razor b/tests/Core/Wizard/FluentWizardTests.razor index 4aedae78c3..94c9f0e293 100644 --- a/tests/Core/Wizard/FluentWizardTests.razor +++ b/tests/Core/Wizard/FluentWizardTests.razor @@ -323,6 +323,8 @@ [Fact] public void FluentWizard_EditForm_EditContextIsInValid_OnNext() { + int stepIndex = 0; + var testRecord1 = new TestRecord { NumberBetween1and10 = 15, @@ -333,7 +335,7 @@ NumberBetween1and10 = 15, }; - var cut = Render(@ + var cut = Render(@ @@ -370,6 +372,7 @@ } Assert.False(finishHandled); + Assert.Equal(0, stepIndex); } [Fact] From f8f0e5a6bf23729b9d3549e44e76a21a9416bfbf Mon Sep 17 00:00:00 2001 From: Jan <41827327+RReventlov@users.noreply.github.com> Date: Tue, 16 Jul 2024 10:53:27 +0200 Subject: [PATCH 09/22] [SortableList] Adding Properties FromListId and ToListId (#2385) * feat: allow FluentSortableList to know where an item was dragged from and dropped to * Renamed properties * Changed example back to two lists The swapping is still handled differently (getting the list through the ids) than before. * Reverted to the previous example with added traces for the id. * Added a test to verify the Id is added to the list * Renamed the test to clarify intent * Added unit test for OnUpdate * Changed parameter names to reflect property names * Reverted changes made by the IDE --------- Co-authored-by: Denis Voituron --- ...crosoft.FluentUI.AspNetCore.Components.xml | 12 +++ .../SortableListMoveBetweenLists.razor | 4 + .../SortableList/FluentSortableList.razor | 2 +- .../SortableList/FluentSortableList.razor.cs | 10 +-- .../SortableList/FluentSortableList.razor.js | 4 +- .../FluentSortableListEventArgs.cs | 16 +++- ...SortableList_IdIsAdded.verified.razor.html | 33 ++++++++ .../FluentSortableListTests.razor | 75 +++++++++++++++++-- 8 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 tests/Core/SortableList/FluentSortableListTests.FluentSortableList_IdIsAdded.verified.razor.html diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml index 3ca6c314d6..785c9b8e82 100644 --- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml +++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml @@ -7959,6 +7959,18 @@ Gets the index of the item in the list after the update.
+ + + Gets the id of the list the item was in before the update. + May be null if no Id was set for + + + + + Gets the id of the list the item is in after the update. + May be null if no Id was set for + + diff --git a/examples/Demo/Shared/Pages/SortableList/Examples/SortableListMoveBetweenLists.razor b/examples/Demo/Shared/Pages/SortableList/Examples/SortableListMoveBetweenLists.razor index 28fd742f45..86cb6b7577 100644 --- a/examples/Demo/Shared/Pages/SortableList/Examples/SortableListMoveBetweenLists.razor +++ b/examples/Demo/Shared/Pages/SortableList/Examples/SortableListMoveBetweenLists.razor @@ -48,6 +48,8 @@ // remove the item from the old index in list 1 items1.Remove(items1[args.OldIndex]); + + DemoLogger.WriteLine($"Moved item from list {args.FromListId} to list {args.ToListId}"); } private void ListTwoRemove(FluentSortableListEventArgs args) @@ -64,6 +66,8 @@ // remove the item from the old index in list 2 items2.Remove(items2[args.OldIndex]); + + DemoLogger.WriteLine($"Moved item from list {args.FromListId} to list {args.ToListId}"); } private void SortListOne(FluentSortableListEventArgs args) { diff --git a/src/Core/Components/SortableList/FluentSortableList.razor b/src/Core/Components/SortableList/FluentSortableList.razor index b4e58b79b5..ebe0da1162 100644 --- a/src/Core/Components/SortableList/FluentSortableList.razor +++ b/src/Core/Components/SortableList/FluentSortableList.razor @@ -7,7 +7,7 @@ @if (ItemTemplate is not null) { -
+
@foreach (var item in Items) {
@ItemTemplate(item)
diff --git a/src/Core/Components/SortableList/FluentSortableList.razor.cs b/src/Core/Components/SortableList/FluentSortableList.razor.cs index 70b3a29911..09b4f3a59a 100644 --- a/src/Core/Components/SortableList/FluentSortableList.razor.cs +++ b/src/Core/Components/SortableList/FluentSortableList.razor.cs @@ -126,22 +126,22 @@ protected bool GetItemFiltered(TItem item) } [JSInvokable] - public void OnUpdateJS(int oldIndex, int newIndex) + public void OnUpdateJS(int oldIndex, int newIndex, string fromListId, string toListId) { if (OnUpdate.HasDelegate) { - // invoke the OnUpdate event passing in the oldIndex and the newIndex - OnUpdate.InvokeAsync(new FluentSortableListEventArgs(oldIndex, newIndex)); + // invoke the OnUpdate event passing in the oldIndex, the newIndex, the fromId and the toId + OnUpdate.InvokeAsync(new FluentSortableListEventArgs(oldIndex, newIndex, fromListId, toListId)); } } [JSInvokable] - public void OnRemoveJS(int oldIndex, int newIndex) + public void OnRemoveJS(int oldIndex, int newIndex, string fromListId, string toListId) { if (OnRemove.HasDelegate) { // remove the item from the list - OnRemove.InvokeAsync(new FluentSortableListEventArgs(oldIndex, newIndex)); + OnRemove.InvokeAsync(new FluentSortableListEventArgs(oldIndex, newIndex, fromListId, toListId)); } } diff --git a/src/Core/Components/SortableList/FluentSortableList.razor.js b/src/Core/Components/SortableList/FluentSortableList.razor.js index 857b417112..324b5e4e0d 100644 --- a/src/Core/Components/SortableList/FluentSortableList.razor.js +++ b/src/Core/Components/SortableList/FluentSortableList.razor.js @@ -16,7 +16,7 @@ export function init(list, group, pull, put, sort, handle, filter, fallback, com event.to.insertBefore(event.item, event.to.childNodes[event.oldIndex]); // Notify .NET to update its model and re-render - component.invokeMethodAsync('OnUpdateJS', event.oldDraggableIndex, event.newDraggableIndex); + component.invokeMethodAsync('OnUpdateJS', event.oldDraggableIndex, event.newDraggableIndex, event.from.id, event.to.id); }, onRemove: (event) => { if (event.pullMode === 'clone') { @@ -28,7 +28,7 @@ export function init(list, group, pull, put, sort, handle, filter, fallback, com event.from.insertBefore(event.item, event.from.childNodes[event.oldIndex]); // Notify .NET to update its model and re-render - component.invokeMethodAsync('OnRemoveJS', event.oldDraggableIndex, event.newDraggableIndex); + component.invokeMethodAsync('OnRemoveJS', event.oldDraggableIndex, event.newDraggableIndex, event.from.id, event.to.id); } }); } diff --git a/src/Core/Components/SortableList/FluentSortableListEventArgs.cs b/src/Core/Components/SortableList/FluentSortableListEventArgs.cs index 07475fbcae..483ed3e23f 100644 --- a/src/Core/Components/SortableList/FluentSortableListEventArgs.cs +++ b/src/Core/Components/SortableList/FluentSortableListEventArgs.cs @@ -7,10 +7,12 @@ public FluentSortableListEventArgs() } - public FluentSortableListEventArgs(int oldIndex, int newIndex) + public FluentSortableListEventArgs(int oldIndex, int newIndex, string? fromListId, string? toListId) { OldIndex = oldIndex; NewIndex = newIndex; + FromListId = fromListId; + ToListId = toListId; } /// @@ -22,4 +24,16 @@ public FluentSortableListEventArgs(int oldIndex, int newIndex) /// Gets the index of the item in the list after the update. /// public int NewIndex { get; } + + /// + /// Gets the id of the list the item was in before the update. + /// May be null if no Id was set for + /// + public string? FromListId { get; } + + /// + /// Gets the id of the list the item is in after the update. + /// May be null if no Id was set for + /// + public string? ToListId { get; } } diff --git a/tests/Core/SortableList/FluentSortableListTests.FluentSortableList_IdIsAdded.verified.razor.html b/tests/Core/SortableList/FluentSortableListTests.FluentSortableList_IdIsAdded.verified.razor.html new file mode 100644 index 0000000000..6f95a3347d --- /dev/null +++ b/tests/Core/SortableList/FluentSortableListTests.FluentSortableList_IdIsAdded.verified.razor.html @@ -0,0 +1,33 @@ + +
+
+
Item 1
+
+
+
Item 2
+
+
+
Item 3
+
+
+
Item 4
+
+
+
Item 5
+
+
+
Item 6
+
+
+
Item 7
+
+
+
Item 8
+
+
+
Item 9
+
+
+
Item 10
+
+
diff --git a/tests/Core/SortableList/FluentSortableListTests.razor b/tests/Core/SortableList/FluentSortableListTests.razor index 17686c32ea..14c623a166 100644 --- a/tests/Core/SortableList/FluentSortableListTests.razor +++ b/tests/Core/SortableList/FluentSortableListTests.razor @@ -1,5 +1,6 @@ @using Xunit; @inherits TestContext + @code { public class Item @@ -23,17 +24,81 @@ public void FluentSortableList_Default() { // Arrange && Act - + var cut = Render(@ - -
@context.Name
-
-
); + +
@context.Name
+
+ ); // Assert cut.Verify(); } + [Fact] + public void FluentSortableList_IdIsAdded() + { + // Arrange && Act + + var cut = Render(@ + +
@context.Name
+
+
); + // Assert + cut.Verify(); + } + + [Fact] + public void FluentSortableList_OnUpdate() + { + // Arrange + FluentSortableListEventArgs? args = null; + var callback = new EventCallbackFactory().Create + (this, (e => args = e)); + var sortableList = new FluentSortableList() + { + Items = items, + OnUpdate = callback + }; + + // Act + sortableList.OnUpdateJS(0, 1, "fromList", "toList"); + + // Verify + Assert.Equal(new FluentSortableListEventArgs(0, 1, "fromList", "toList"), args, + FluentSortableListEventArgsComparer); + } + + private static bool FluentSortableListEventArgsComparer(FluentSortableListEventArgs? first, FluentSortableListEventArgs? second) + { + if (first is null || second is null) + { + return false; + } + + if (first.FromListId is null || !first.FromListId.Equals(second.FromListId)) + { + return false; + } + + if (first.ToListId is null || !first.ToListId.Equals(second.ToListId)) + { + return false; + } + + if (first.OldIndex != second.OldIndex) + { + return false; + } + + if (first.NewIndex != second.NewIndex) + { + return false; + } + + return true; + } } From adf2b77d172f4b347e05b5c98937deea278f4a9a Mon Sep 17 00:00:00 2001 From: Daniele Corsini Date: Tue, 16 Jul 2024 22:59:29 +0200 Subject: [PATCH 10/22] [Docs] Improve API Documentation performance (#2377) * Improve documentation * Add space --------- Co-authored-by: Vincent Baaij --- .../Shared/Components/ApiDocumentation.razor | 2 +- .../Components/ApiDocumentation.razor.cs | 56 +++++++++++-------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/examples/Demo/Shared/Components/ApiDocumentation.razor b/examples/Demo/Shared/Components/ApiDocumentation.razor index cffacb48ed..f27addbad0 100644 --- a/examples/Demo/Shared/Components/ApiDocumentation.razor +++ b/examples/Demo/Shared/Components/ApiDocumentation.razor @@ -53,7 +53,7 @@ { var id = Identifier.NewId(); -
Preview:
+
Preview: 
@context.Default diff --git a/examples/Demo/Shared/Components/ApiDocumentation.razor.cs b/examples/Demo/Shared/Components/ApiDocumentation.razor.cs index 194dacbafb..156e525ebf 100644 --- a/examples/Demo/Shared/Components/ApiDocumentation.razor.cs +++ b/examples/Demo/Shared/Components/ApiDocumentation.razor.cs @@ -13,8 +13,8 @@ private class MemberDescription public MemberTypes MemberType { get; set; } = MemberTypes.Property; public string Name { get; set; } = ""; public string Type { get; set; } = ""; - public string[] EnumValues { get; set; } = Array.Empty(); - public string[] Parameters { get; set; } = Array.Empty(); + public string[] EnumValues { get; set; } = []; + public string[] Parameters { get; set; } = []; public string? Default { get; set; } = null; public string Description { get; set; } = ""; public bool IsParameter { get; set; } @@ -39,7 +39,7 @@ private class MemberDescription /// Default for this parameter is 'typeof(string)' ///
[Parameter] - public Type[] InstanceTypes { get; set; } = new[] { typeof(string) }; + public Type[] InstanceTypes { get; set; } = [typeof(string)]; /// /// Gets or sets the label used for displaying the type parameter. @@ -70,29 +70,37 @@ private IEnumerable GetMembers(MemberTypes type) { List? members = []; - object? obj; - if (Component.IsGenericType) + object? obj = null; + var created = false; + + object? GetObjectValue(string propertyName) { - if (InstanceTypes is null) + if (!created) { - throw new ArgumentNullException(nameof(InstanceTypes), "InstanceTypes must be specified when Component is a generic type"); - } + if (Component.IsGenericType) + { + if (InstanceTypes is null) + { + throw new ArgumentNullException(nameof(InstanceTypes), "InstanceTypes must be specified when Component is a generic type"); + } - // Supply the type to create the generic instance with (needs to be an array) - Type[] typeArgs = InstanceTypes; - Type constructed = Component.MakeGenericType(typeArgs); + // Supply the type to create the generic instance with (needs to be an array) + obj = Activator.CreateInstance(Component.MakeGenericType(InstanceTypes)); + } + else + { + obj = Activator.CreateInstance(Component); + } + created = true; + } - obj = Activator.CreateInstance(constructed); - } - else - { - obj = Activator.CreateInstance(Component); - } + return obj?.GetType().GetProperty(propertyName)?.GetValue(obj); + }; - IEnumerable? allProperties = Component.GetProperties().Select(i => (MemberInfo)i); - IEnumerable? allMethods = Component.GetMethods().Where(i => !i.IsSpecialName).Select(i => (MemberInfo)i); + var allProperties = Component.GetProperties().Select(i => (MemberInfo)i); + var allMethods = Component.GetMethods().Where(i => !i.IsSpecialName).Select(i => (MemberInfo)i); - foreach (MemberInfo memberInfo in allProperties.Union(allMethods).OrderBy(m => m.Name)) + foreach (var memberInfo in allProperties.Union(allMethods).OrderBy(m => m.Name)) { try { @@ -105,7 +113,7 @@ private IEnumerable GetMembers(MemberTypes type) { var isParameter = memberInfo.GetCustomAttribute() != null; - Type t = propertyInfo.PropertyType; + var t = propertyInfo.PropertyType; var isEvent = t == typeof(EventCallback) || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(EventCallback<>)); // Parameters/properties @@ -115,11 +123,11 @@ private IEnumerable GetMembers(MemberTypes type) var defaultVaue = ""; if (propertyInfo.PropertyType.IsValueType || propertyInfo.PropertyType == typeof(string)) { - defaultVaue = obj?.GetType().GetProperty(propertyInfo.Name)?.GetValue(obj)?.ToString(); + defaultVaue = GetObjectValue(propertyInfo.Name)?.ToString(); } else if (propertyInfo.PropertyType == typeof(Icon)) { - if (obj?.GetType().GetProperty(propertyInfo.Name)?.GetValue(obj) is Icon value) + if (GetObjectValue(propertyInfo.Name) is Icon value) { icon = value; defaultVaue = $"{value.Variant}.{value.Size}.{value.Name}"; @@ -201,6 +209,6 @@ private static string[] GetEnumValues(PropertyInfo? propertyInfo) } } - return Array.Empty(); + return []; } } From 8b8e4899f6b263ddb08280c4c522934d93a4c804 Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Tue, 16 Jul 2024 23:57:18 +0200 Subject: [PATCH 11/22] Fix #2387 by removing '>' from class specifications --- src/Core/Components/NavMenu/FluentNavMenu.razor.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Core/Components/NavMenu/FluentNavMenu.razor.css b/src/Core/Components/NavMenu/FluentNavMenu.razor.css index 5c4054090e..15f8c5cfa2 100644 --- a/src/Core/Components/NavMenu/FluentNavMenu.razor.css +++ b/src/Core/Components/NavMenu/FluentNavMenu.razor.css @@ -72,19 +72,19 @@ } /* level indenting */ -::deep .fluent-nav-group * .fluent-nav-menu > .fluent-nav-item .content-region { +::deep .fluent-nav-group * .fluent-nav-menu .fluent-nav-item .content-region { padding-inline-start: 24px; } -::deep .fluent-nav-group * .fluent-nav-menu > .fluent-nav-item * .fluent-nav-menu > .fluent-nav-item .content-region { +::deep .fluent-nav-group * .fluent-nav-menu .fluent-nav-item * .fluent-nav-menu .fluent-nav-item .content-region { padding-inline-start: 48px; } -::deep .fluent-nav-group * .fluent-nav-menu > .fluent-nav-item * .fluent-nav-menu > .fluent-nav-item * .fluent-nav-menu > .fluent-nav-item .content-region { +::deep .fluent-nav-group * .fluent-nav-menu .fluent-nav-item * .fluent-nav-menu .fluent-nav-item * .fluent-nav-menu .fluent-nav-item .content-region { padding-inline-start: 72px; } -::deep .fluent-nav-group * .fluent-nav-menu > .fluent-nav-item * .fluent-nav-menu > .fluent-nav-item * .fluent-nav-menu > .fluent-nav-item * .fluent-nav-menu > .fluent-nav-item .content-region { +::deep .fluent-nav-group * .fluent-nav-menu .fluent-nav-item * .fluent-nav-menu .fluent-nav-item * .fluent-nav-menu .fluent-nav-item * .fluent-nav-menu .fluent-nav-item .content-region { padding-inline-start: 96px; } From 885a03149f2a0351b45b7c8730049aed2fdeff05 Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Wed, 17 Jul 2024 11:15:54 +0200 Subject: [PATCH 12/22] Fix #2392 by passing Class through to web component --- .../DesignSystemProvider/FluentDesignSystemProvider.razor | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Core/Components/DesignSystemProvider/FluentDesignSystemProvider.razor b/src/Core/Components/DesignSystemProvider/FluentDesignSystemProvider.razor index b60beee477..b65422d145 100644 --- a/src/Core/Components/DesignSystemProvider/FluentDesignSystemProvider.razor +++ b/src/Core/Components/DesignSystemProvider/FluentDesignSystemProvider.razor @@ -1,7 +1,8 @@ -@namespace Microsoft.FluentUI.AspNetCore.Components +@namespace Microsoft.FluentUI.AspNetCore.Components @inherits FluentComponentBase - Date: Wed, 17 Jul 2024 15:47:10 +0200 Subject: [PATCH 13/22] [JavaScript Caching] Cache management (#2388) * First tests * Update Unit Tests * Update program.cs * Fix XML Comments * Update to CollocatedJavaScriptQueryString * Inject LibraryConfiguration in all associated components and Unit Tests * Add FormatCollocatedUrl extension method * Add URL argument in CollocatedJavaScriptQueryString method * Update FluentTooltip injected service --- ...crosoft.FluentUI.AspNetCore.Components.xml | 126 +++++++++++++++++- .../Components/Anchor/FluentAnchor.razor.cs | 12 +- .../FluentAnchoredRegion.razor.cs | 8 +- .../Components/AppBar/FluentAppBar.razor.cs | 7 +- .../Components/Button/FluentButton.razor.cs | 7 +- .../Checkbox/FluentCheckbox.razor.cs | 7 +- .../DataGrid/FluentDataGrid.razor.cs | 63 ++++++--- .../FluentDesignTheme.razor.cs | 9 +- .../Components/Divider/FluentDivider.razor.cs | 8 +- src/Core/Components/Grid/FluentGrid.razor.cs | 7 +- .../FluentHorizontalScroll.razor.cs | 19 ++- .../InputFile/FluentInputFile.razor.cs | 9 +- .../Components/KeyCode/FluentKeyCode.razor.cs | 7 +- .../Components/Label/FluentInputLabel.razor | 2 - .../Label/FluentInputLabel.razor.cs | 7 +- .../List/FluentAutocomplete.razor.cs | 7 +- .../Components/List/FluentCombobox.razor.cs | 7 +- .../List/ListComponentBase.razor.cs | 6 +- src/Core/Components/Menu/FluentMenu.razor.cs | 10 +- .../Overflow/FluentOverflow.razor.cs | 7 +- .../FluentPullToRefresh.razor.cs | 7 +- .../Components/Search/FluentSearch.razor.cs | 7 +- .../Slider/FluentSliderLabel.razor.cs | 7 +- .../SortableList/FluentSortableList.razor.cs | 14 +- .../Splitter/FluentMultiSplitter.razor.cs | 21 +-- src/Core/Components/Tabs/FluentTab.razor.cs | 9 +- src/Core/Components/Tabs/FluentTabs.razor.cs | 10 +- .../TextField/FluentTextField.razor.cs | 10 +- .../Components/Tooltip/FluentTooltip.razor.cs | 5 +- src/Core/DesignTokens/DesignToken.razor.cs | 7 +- src/Core/Extensions/UrlFormatterExtensions.cs | 17 +++ .../Infrastructure/LibraryConfiguration.cs | 19 +++ tests/Core/Anchor/FluentAnchorTests.cs | 2 + .../FluentAnchoredRegionTests.cs | 2 + tests/Core/AppBar/FluentAppBarItemTests.razor | 1 + tests/Core/AppBar/FluentAppBarTests.razor | 1 + tests/Core/Button/FluentButtonTests.cs | 56 ++++---- tests/Core/Checkbox/FluentCheckboxTests.cs | 6 + .../FluentCheckboxThreeStatesTests.razor | 21 ++- tests/Core/DataGrid/DataGridSortByTests.razor | 1 + .../FluentDataGridColumSelectTests.razor.cs | 1 + tests/Core/Divider/FluentDividerTests.razor | 19 ++- tests/Core/Grid/FluentGridTests.razor | 3 +- tests/Core/Icons/FluentIconTests.razor | 5 + .../Core/InputFile/FluentInputFileTests.razor | 7 +- .../KeyCodeProvider/FluentKeyCodeTests.razor | 20 +-- tests/Core/NavMenu/FluentNavGroupTests.cs | 2 + tests/Core/NavMenu/FluentNavMenuTests.cs | 2 + .../ProfileMenu/FluentProfileMenuTests.razor | 1 + .../FluentPullToRefreshTests.razor | 1 + tests/Core/Slider/FluentSliderLaberTests.cs | 2 + .../FluentSortableListTests.razor | 4 +- .../Splitter/FluentMultiSplitterTests.razor | 43 ++---- tests/Core/Utilities/JSModuleTests.cs | 81 ----------- .../FluentHorizontalScrollTests.cs | 3 + .../_ToDo/MainLayout/FluentMainLayoutTests.cs | 2 + tests/Core/_ToDo/Menu/FluentMenuTests.cs | 2 + .../_ToDo/MenuButton/FluentMenuButtonTests.cs | 7 + 58 files changed, 503 insertions(+), 260 deletions(-) create mode 100644 src/Core/Extensions/UrlFormatterExtensions.cs delete mode 100644 tests/Core/Utilities/JSModuleTests.cs diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml index 785c9b8e82..45fdf8cdac 100644 --- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml +++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml @@ -95,6 +95,12 @@ Gets or sets the content to be rendered inside the component.
+ + + + + + @@ -224,6 +230,12 @@ + + + + + + Prompts the user to save the linked URL. See a element for more information. @@ -290,6 +302,9 @@ + + + @@ -796,6 +811,9 @@ Gets or sets the content to be rendered inside the component. + + + @@ -956,6 +974,9 @@ Gets or sets the content to be rendered inside the component. + + + @@ -1712,6 +1733,9 @@ The type of data represented by each row in the grid. + + + Gets or sets a queryable source of data for the grid. @@ -2946,6 +2970,12 @@ Gets or sets the content to be rendered inside the component. + + + + + + @@ -3829,6 +3859,12 @@ An event that will be invoked when showing a dialog with a custom component + + + + + + Gets or sets the role of the element. @@ -4194,6 +4230,9 @@ that are used for specific screen sizes. + + + @@ -4354,6 +4393,12 @@ If true, highlights the text until the next regex boundary. + + + + + + Gets or sets the scroll speed in pixels per second. @@ -4583,6 +4628,9 @@ + + + @@ -4833,6 +4881,9 @@ Prevent multiple KeyDown events. + + + @@ -5108,6 +5159,9 @@ Private KeyCodeListener + + + @@ -5216,6 +5270,9 @@ Initializes a new instance of the class. + + + @@ -5409,6 +5466,9 @@ + + + @@ -5597,6 +5657,9 @@ + + + @@ -5835,6 +5898,9 @@ + + + @@ -6876,6 +6942,9 @@ + + + Gets or sets the content to display. @@ -7487,6 +7556,9 @@ + + + Gets or sets the direction to pull the . @@ -7736,6 +7808,9 @@ + + + @@ -7859,6 +7934,9 @@ The value to format. A string representation of the value. + + + @@ -7888,6 +7966,12 @@ + + + + + + Gets or sets the template to be used to define each sortable item in the list. @@ -7979,6 +8063,15 @@ Gets or sets the width of the spacer (in pixels). + + + + + + + + + Gets or sets the child content. @@ -8030,12 +8123,6 @@ - - - - - - Adds the pane. @@ -8334,6 +8421,9 @@ + + + @@ -8438,6 +8528,9 @@ + + + @@ -8587,6 +8680,9 @@ Gets or sets the content to be rendered inside the component. + + + @@ -9616,6 +9712,9 @@ + + + Gets the name of this design token @@ -14498,6 +14597,11 @@ Defines the global Fluent UI Blazor component library services configuration + + + Gets the assembly version formatted as a string. + + Gets or sets a value indicating whether the library should use the TooltipServiceProvider. @@ -14522,6 +14626,16 @@ Default is true. + + + Gets or sets the function that formats the URL of the collocated JavaScript file, + adding the return value as a query string parameter. + By default, the function adds a query string parameter with the version of the assembly: `v=[AssemblyVersion]`. + + + + + A strongly-typed resource class, for looking up localized strings, etc. diff --git a/src/Core/Components/Anchor/FluentAnchor.razor.cs b/src/Core/Components/Anchor/FluentAnchor.razor.cs index c706d1bda1..4d3d70ef00 100644 --- a/src/Core/Components/Anchor/FluentAnchor.razor.cs +++ b/src/Core/Components/Anchor/FluentAnchor.razor.cs @@ -1,17 +1,24 @@ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; public partial class FluentAnchor : FluentComponentBase, IAsyncDisposable { + private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Anchor/FluentAnchor.razor.js"; private IJSObjectReference _jsModule = default!; private string? _targetId = null; private bool _preventDefault = false; + /// [Inject] - protected IJSRuntime JSRuntime { get; set; } = default!; + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + + /// + [Inject] + private IJSRuntime JSRuntime { get; set; } = default!; /// /// Prompts the user to save the linked URL. See a element for more information. @@ -123,8 +130,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - _jsModule = await JSRuntime.InvokeAsync("import", - "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Anchor/FluentAnchor.razor.js"); + _jsModule = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); } } diff --git a/src/Core/Components/AnchoredRegion/FluentAnchoredRegion.razor.cs b/src/Core/Components/AnchoredRegion/FluentAnchoredRegion.razor.cs index 7a60e950c2..cabea0cd47 100644 --- a/src/Core/Components/AnchoredRegion/FluentAnchoredRegion.razor.cs +++ b/src/Core/Components/AnchoredRegion/FluentAnchoredRegion.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -7,6 +8,11 @@ public partial class FluentAnchoredRegion : FluentComponentBase { private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/AnchoredRegion/FluentAnchoredRegion.razor.js"; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -161,7 +167,7 @@ protected async override Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); // Go to the first Focusable element (after a delay of 100ms, to allow the DOM to render the Web Components) if (AutoFocus) diff --git a/src/Core/Components/AppBar/FluentAppBar.razor.cs b/src/Core/Components/AppBar/FluentAppBar.razor.cs index 50570749db..fc77f4bc3a 100644 --- a/src/Core/Components/AppBar/FluentAppBar.razor.cs +++ b/src/Core/Components/AppBar/FluentAppBar.razor.cs @@ -3,6 +3,7 @@ // ------------------------------------------------------------------------ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; using System.Text.Json; @@ -23,6 +24,10 @@ public partial class FluentAppBar : FluentComponentBase // ToDo: Implement focus on popup //private FluentSearch? _appSearch; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -77,7 +82,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { _dotNetHelper = DotNetObjectReference.Create(this); // Overflow - _jsModuleOverflow = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModuleOverflow = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await _jsModuleOverflow.InvokeVoidAsync("fluentOverflowInitialize", _dotNetHelper, Id, false, OVERFLOW_SELECTOR); } diff --git a/src/Core/Components/Button/FluentButton.razor.cs b/src/Core/Components/Button/FluentButton.razor.cs index ef4a476bbb..a52ecebef4 100644 --- a/src/Core/Components/Button/FluentButton.razor.cs +++ b/src/Core/Components/Button/FluentButton.razor.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -11,6 +12,10 @@ public partial class FluentButton : FluentComponentBase, IAsyncDisposable private readonly RenderFragment _renderButton; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -180,7 +185,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && Id is not null && Type != ButtonType.Button) { - _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await _jsModule.InvokeVoidAsync("updateProxy", Id); } } diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs index b53f032063..0911e08b4b 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -17,6 +18,10 @@ public FluentCheckbox() Id = Identifier.NewId(); } + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -122,7 +127,7 @@ private async Task SetIntermediateAsync(bool intermediate) { // Force the Indeterminate state to be set. // Each time the user clicks the checkbox, the Indeterminate state is reset to false. - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("setFluentCheckBoxIndeterminate", Id, intermediate, Value); _intermediate = intermediate; diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs index 83b5c2d984..90178a26dd 100644 --- a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs +++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web.Virtualization; using Microsoft.FluentUI.AspNetCore.Components.DataGrid.Infrastructure; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Infrastructure; using Microsoft.JSInterop; using System.Diagnostics.CodeAnalysis; @@ -19,6 +20,20 @@ namespace Microsoft.FluentUI.AspNetCore.Components; public partial class FluentDataGrid : FluentComponentBase, IHandleEvent, IAsyncDisposable { private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/DataGrid/FluentDataGrid.razor.js"; + + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + + [Inject] + private IServiceProvider Services { get; set; } = default!; + + [Inject] + private IJSRuntime JSRuntime { get; set; } = default!; + + [Inject] + private IKeyCodeService KeyCodeService { get; set; } = default!; + /// /// Gets or sets a queryable source of data for the grid. /// @@ -28,20 +43,23 @@ public partial class FluentDataGrid : FluentComponentBase, IHandleEve /// /// You should supply either or , but not both. /// - [Parameter] public IQueryable? Items { get; set; } + [Parameter] + public IQueryable? Items { get; set; } /// /// Gets or sets a callback that supplies data for the rid. /// /// You should supply either or , but not both. /// - [Parameter] public GridItemsProvider? ItemsProvider { get; set; } + [Parameter] + public GridItemsProvider? ItemsProvider { get; set; } /// /// Gets or sets the child components of this instance. For example, you may define columns by adding /// components derived from the base class. /// - [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] + public RenderFragment? ChildContent { get; set; } /// /// If true, the grid will be rendered with virtualization. This is normally used in conjunction with @@ -54,20 +72,23 @@ public partial class FluentDataGrid : FluentComponentBase, IHandleEve /// Generally it's preferable not to use if the amount of data being rendered /// is small or if you are using pagination. /// - [Parameter] public bool Virtualize { get; set; } + [Parameter] + public bool Virtualize { get; set; } /// /// This is applicable only when using . It defines an expected height in pixels for /// each row, allowing the virtualization mechanism to fetch the correct number of items to match the display /// size and to ensure accurate scrolling. /// - [Parameter] public float ItemSize { get; set; } = 32; + [Parameter] + public float ItemSize { get; set; } = 32; /// /// If true, renders draggable handles around the column headers, allowing the user to resize the columns /// manually. Size changes are not persisted. /// - [Parameter] public bool ResizableColumns { get; set; } + [Parameter] + public bool ResizableColumns { get; set; } /// /// To comply with WCAG 2.2, a one-click option should be offered to change column widths. We provide such an option through the @@ -91,7 +112,8 @@ public partial class FluentDataGrid : FluentComponentBase, IHandleEve /// /// If not set, the @key will be the instance itself. /// - [Parameter] public Func ItemKey { get; set; } = x => x!; + [Parameter] + public Func ItemKey { get; set; } = x => x!; /// /// Optionally links this instance with a model, @@ -100,7 +122,8 @@ public partial class FluentDataGrid : FluentComponentBase, IHandleEve /// This is normally used in conjunction with a component or some other UI logic /// that displays and updates the supplied instance. /// - [Parameter] public PaginationState? Pagination { get; set; } + [Parameter] + public PaginationState? Pagination { get; set; } /// /// Gets or sets a value indicating whether the component will not add itself to the tab queue. @@ -157,38 +180,40 @@ public partial class FluentDataGrid : FluentComponentBase, IHandleEve /// /// Optionally defines a class to be applied to a rendered row. /// - [Parameter] public Func? RowClass { get; set; } + [Parameter] + public Func? RowClass { get; set; } /// /// Optionally defines a style to be applied to a rendered row. /// Do not use to dynamically update a row style after rendering as this will interfere with the script that use this attribute. Use instead. /// - [Parameter] public Func? RowStyle { get; set; } + [Parameter] + public Func? RowStyle { get; set; } /// /// Gets or sets a value indicating whether the grid should show a hover effect on rows. /// - [Parameter] public bool ShowHover { get; set; } + [Parameter] + public bool ShowHover { get; set; } /// /// If specified, grids render this fragment when there is no content. /// - [Parameter] public RenderFragment? EmptyContent { get; set; } + [Parameter] + public RenderFragment? EmptyContent { get; set; } /// /// Gets or sets a value indicating whether the grid is in a loading data state. /// - [Parameter] public bool Loading { get; set; } + [Parameter] + public bool Loading { get; set; } /// /// Gets or sets the content to render when is true. /// A default fragment is used if loading content is not specified. /// - [Parameter] public RenderFragment? LoadingContent { get; set; } - - [Inject] private IServiceProvider Services { get; set; } = default!; - [Inject] private IJSRuntime JSRuntime { get; set; } = default!; - [Inject] private IKeyCodeService KeyCodeService { get; set; } = default!; + [Parameter] + public RenderFragment? LoadingContent { get; set; } private ElementReference? _gridReference; private Virtualize<(int, TGridItem)>? _virtualizeComponent; @@ -299,7 +324,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && _gridReference is not null) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); try { _jsEventDisposable = await Module.InvokeAsync("init", _gridReference); diff --git a/src/Core/Components/DesignSystemProvider/FluentDesignTheme.razor.cs b/src/Core/Components/DesignSystemProvider/FluentDesignTheme.razor.cs index b68b7cd709..cdadb36c4a 100644 --- a/src/Core/Components/DesignSystemProvider/FluentDesignTheme.razor.cs +++ b/src/Core/Components/DesignSystemProvider/FluentDesignTheme.razor.cs @@ -16,6 +16,11 @@ public partial class FluentDesignTheme : ComponentBase PropertyNameCaseInsensitive = true, }; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + + /// [Inject] private GlobalState GlobalDesign { get; set; } = default!; @@ -174,7 +179,7 @@ protected async override Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); _dotNetHelper = DotNetObjectReference.Create(this); var dir = await Module.InvokeAsync("GetDirection"); @@ -210,7 +215,7 @@ protected async override Task OnAfterRenderAsync(bool firstRender) /// public async Task ClearLocalStorageAsync() { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("ClearLocalStorage", Id); } diff --git a/src/Core/Components/Divider/FluentDivider.razor.cs b/src/Core/Components/Divider/FluentDivider.razor.cs index e10660db04..3ca9f962d1 100644 --- a/src/Core/Components/Divider/FluentDivider.razor.cs +++ b/src/Core/Components/Divider/FluentDivider.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -9,6 +10,11 @@ public partial class FluentDivider : FluentComponentBase, IAsyncDisposable private IJSObjectReference _jsModule = default!; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + + /// [Inject] protected IJSRuntime JSRuntime { get; set; } = default!; @@ -34,7 +40,7 @@ protected async override Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await _jsModule.InvokeVoidAsync("setDividerAriaOrientation"); } } diff --git a/src/Core/Components/Grid/FluentGrid.razor.cs b/src/Core/Components/Grid/FluentGrid.razor.cs index cff5be38f6..cfc18e0156 100644 --- a/src/Core/Components/Grid/FluentGrid.razor.cs +++ b/src/Core/Components/Grid/FluentGrid.razor.cs @@ -2,6 +2,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // -------------------------------------------------------------- using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -21,6 +22,10 @@ public FluentGrid() Id = Identifier.NewId(); } + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -75,7 +80,7 @@ protected async override Task OnAfterRenderAsync(bool firstRender) { if (firstRender && OnBreakpointEnter.HasDelegate) { - _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); DotNetObjectReference dotNetHelper = DotNetObjectReference.Create(this); await _jsModule.InvokeVoidAsync("FluentGridInitialize", Id, dotNetHelper); } diff --git a/src/Core/Components/HorizontalScroll/FluentHorizontalScroll.razor.cs b/src/Core/Components/HorizontalScroll/FluentHorizontalScroll.razor.cs index 87e91a9e9a..277d092313 100644 --- a/src/Core/Components/HorizontalScroll/FluentHorizontalScroll.razor.cs +++ b/src/Core/Components/HorizontalScroll/FluentHorizontalScroll.razor.cs @@ -1,11 +1,24 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; public partial class FluentHorizontalScroll : FluentComponentBase, IAsyncDisposable { + private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/HorizontalScroll/FluentHorizontalScroll.razor.js"; + + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + + /// + [Inject] + private IJSRuntime JSRuntime { get; set; } = default!; + + private IJSObjectReference? _jsModule; + /// /// Gets or sets the scroll speed in pixels per second. /// @@ -42,10 +55,6 @@ public partial class FluentHorizontalScroll : FluentComponentBase, IAsyncDisposa [Parameter] public RenderFragment? ChildContent { get; set; } - [Inject] private IJSRuntime JSRuntime { get; set; } = default!; - - private IJSObjectReference? _jsModule; - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(HorizontalScrollEventArgs))] public FluentHorizontalScroll() @@ -56,7 +65,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - _jsModule = await JSRuntime.InvokeAsync("import", "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/HorizontalScroll/FluentHorizontalScroll.razor.js"); + _jsModule = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); } } diff --git a/src/Core/Components/InputFile/FluentInputFile.razor.cs b/src/Core/Components/InputFile/FluentInputFile.razor.cs index 5510f39371..7a02ecb001 100644 --- a/src/Core/Components/InputFile/FluentInputFile.razor.cs +++ b/src/Core/Components/InputFile/FluentInputFile.razor.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -21,6 +22,10 @@ public FluentInputFile() Id = Identifier.NewId(); } + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -180,7 +185,7 @@ public FluentInputFile() /// public async Task ShowFilesDialogAsync() { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("raiseFluentInputFile", Id); } @@ -190,7 +195,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && !string.IsNullOrEmpty(AnchorId)) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("attachClickHandler", AnchorId, Id); } diff --git a/src/Core/Components/KeyCode/FluentKeyCode.razor.cs b/src/Core/Components/KeyCode/FluentKeyCode.razor.cs index eea3d11981..a8b20a3313 100644 --- a/src/Core/Components/KeyCode/FluentKeyCode.razor.cs +++ b/src/Core/Components/KeyCode/FluentKeyCode.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -19,6 +20,10 @@ public partial class FluentKeyCode : IAsyncDisposable /// public static bool PreventMultipleKeyDown = false; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -114,7 +119,7 @@ protected async override Task OnAfterRenderAsync(bool firstRender) throw new ArgumentNullException(Anchor, $"The {nameof(Anchor)} parameter must be set to the ID of an element. Or the {nameof(ChildContent)} must be set to apply the KeyCode engine to this content."); } - _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); _dotNetHelper = DotNetObjectReference.Create(this); var eventNames = string.Join(";", new[] diff --git a/src/Core/Components/Label/FluentInputLabel.razor b/src/Core/Components/Label/FluentInputLabel.razor index f5d076dbea..3453463105 100644 --- a/src/Core/Components/Label/FluentInputLabel.razor +++ b/src/Core/Components/Label/FluentInputLabel.razor @@ -1,6 +1,4 @@ @namespace Microsoft.FluentUI.AspNetCore.Components -@inject LibraryConfiguration LibraryConfiguration - @if (!string.IsNullOrEmpty(Label) || ChildContent is not null) { diff --git a/src/Core/Components/Label/FluentInputLabel.razor.cs b/src/Core/Components/Label/FluentInputLabel.razor.cs index af5980f01d..403eebd146 100644 --- a/src/Core/Components/Label/FluentInputLabel.razor.cs +++ b/src/Core/Components/Label/FluentInputLabel.razor.cs @@ -3,6 +3,7 @@ // ------------------------------------------------------------------------ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -11,6 +12,10 @@ public partial class FluentInputLabel { public const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Label/FluentInputLabel.razor.js"; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -72,7 +77,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (firstRender && ShouldRenderAriaLabel) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("setInputAriaLabel", ForId, string.IsNullOrWhiteSpace(AriaLabel) ? Label : AriaLabel); } } diff --git a/src/Core/Components/List/FluentAutocomplete.razor.cs b/src/Core/Components/List/FluentAutocomplete.razor.cs index 9d7f08728d..e5155e0788 100644 --- a/src/Core/Components/List/FluentAutocomplete.razor.cs +++ b/src/Core/Components/List/FluentAutocomplete.razor.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web.Virtualization; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -29,6 +30,10 @@ public FluentAutocomplete() Id = Identifier.NewId(); } + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JS { get; set; } = default!; @@ -487,7 +492,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - Module = await JS.InvokeAsync("import", JAVASCRIPT_FILE); + Module = await JS.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); } } diff --git a/src/Core/Components/List/FluentCombobox.razor.cs b/src/Core/Components/List/FluentCombobox.razor.cs index 360ccc259e..6caeb553e4 100644 --- a/src/Core/Components/List/FluentCombobox.razor.cs +++ b/src/Core/Components/List/FluentCombobox.razor.cs @@ -3,6 +3,7 @@ // ------------------------------------------------------------------------ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -13,6 +14,10 @@ public partial class FluentCombobox : ListComponentBase where { private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/List/FluentCombobox.razor.js"; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -57,7 +62,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (!string.IsNullOrEmpty(Id)) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("setControlAttribute", Id, "autocomplete", "off"); } } diff --git a/src/Core/Components/List/ListComponentBase.razor.cs b/src/Core/Components/List/ListComponentBase.razor.cs index 993739c062..a1bf82e3df 100644 --- a/src/Core/Components/List/ListComponentBase.razor.cs +++ b/src/Core/Components/List/ListComponentBase.razor.cs @@ -26,6 +26,10 @@ public abstract partial class ListComponentBase : FluentInputBase + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + private IJSObjectReference? _jsModule { get; set; } [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -38,7 +42,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); } } diff --git a/src/Core/Components/Menu/FluentMenu.razor.cs b/src/Core/Components/Menu/FluentMenu.razor.cs index f570966de3..ff5ea9822e 100644 --- a/src/Core/Components/Menu/FluentMenu.razor.cs +++ b/src/Core/Components/Menu/FluentMenu.razor.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Drawing; using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -8,6 +9,8 @@ namespace Microsoft.FluentUI.AspNetCore.Components; public partial class FluentMenu : FluentComponentBase, IDisposable { + private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Menu/FluentMenu.razor.js"; + private DotNetObjectReference? _dotNetHelper = null; private Point _clickedPoint = default; private bool _contextMenu = false; @@ -33,6 +36,10 @@ public partial class FluentMenu : FluentComponentBase, IDisposable .AddStyle("top", $"{_clickedPoint.Y}px", () => !Anchored && _clickedPoint.Y != 0) .Build(); + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -134,8 +141,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (Trigger != MouseButton.None) { - _jsModule = await JSRuntime.InvokeAsync("import", - "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Menu/FluentMenu.razor.js"); + _jsModule = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); _dotNetHelper = DotNetObjectReference.Create(this); diff --git a/src/Core/Components/Overflow/FluentOverflow.razor.cs b/src/Core/Components/Overflow/FluentOverflow.razor.cs index e8ccf4119c..076c422795 100644 --- a/src/Core/Components/Overflow/FluentOverflow.razor.cs +++ b/src/Core/Components/Overflow/FluentOverflow.razor.cs @@ -3,6 +3,7 @@ // ------------------------------------------------------------------------ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; using System.Text.Json; @@ -27,6 +28,10 @@ public partial class FluentOverflow : FluentComponentBase, IAsyncDisposable .AddStyle("visibility", "hidden", VisibleOnLoad == false) .Build(); + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + [Inject] protected IJSRuntime JSRuntime { get; set; } = default!; @@ -93,7 +98,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - _jsModule = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); _dotNetHelper = DotNetObjectReference.Create(this); await _jsModule.InvokeVoidAsync("fluentOverflowInitialize", _dotNetHelper, Id, IsHorizontal, Selectors); VisibleOnLoad = true; diff --git a/src/Core/Components/PullToRefresh/FluentPullToRefresh.razor.cs b/src/Core/Components/PullToRefresh/FluentPullToRefresh.razor.cs index dd9f6641d1..3265bc6d87 100644 --- a/src/Core/Components/PullToRefresh/FluentPullToRefresh.razor.cs +++ b/src/Core/Components/PullToRefresh/FluentPullToRefresh.razor.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -30,6 +31,10 @@ public partial class FluentPullToRefresh : FluentComponentBase protected string? StyleValue => new StyleBuilder(Style) .Build(); + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + [Inject] protected IJSRuntime JSRuntime { get; set; } = default!; @@ -171,7 +176,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && EmulateTouch) { - _jsModule = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await _jsModule.InvokeVoidAsync("initTouchEmulator"); } diff --git a/src/Core/Components/Search/FluentSearch.razor.cs b/src/Core/Components/Search/FluentSearch.razor.cs index 1f06b76cb3..a901901fc8 100644 --- a/src/Core/Components/Search/FluentSearch.razor.cs +++ b/src/Core/Components/Search/FluentSearch.razor.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -8,6 +9,10 @@ public partial class FluentSearch : FluentInputBase { private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Search/FluentSearch.razor.js"; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -76,7 +81,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { //if (!string.IsNullOrEmpty(Id)) //{ - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("addAriaHidden", Id); //} } diff --git a/src/Core/Components/Slider/FluentSliderLabel.razor.cs b/src/Core/Components/Slider/FluentSliderLabel.razor.cs index 0ab768aa82..53c6d18b31 100644 --- a/src/Core/Components/Slider/FluentSliderLabel.razor.cs +++ b/src/Core/Components/Slider/FluentSliderLabel.razor.cs @@ -1,5 +1,6 @@ using System.Globalization; using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -13,6 +14,10 @@ public FluentSliderLabel() Id = Identifier.NewId(); } + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -65,7 +70,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await _jsModule.InvokeVoidAsync("updateSliderLabel", Id); } } diff --git a/src/Core/Components/SortableList/FluentSortableList.razor.cs b/src/Core/Components/SortableList/FluentSortableList.razor.cs index 09b4f3a59a..5dd737c058 100644 --- a/src/Core/Components/SortableList/FluentSortableList.razor.cs +++ b/src/Core/Components/SortableList/FluentSortableList.razor.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -10,6 +11,14 @@ public partial class FluentSortableList : FluentComponentBase private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/SortableList/FluentSortableList.razor.js"; private DotNetObjectReference>? _selfReference; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + + /// + [Inject] + private IJSRuntime JSRuntime { get; set; } = default!; + /// /// Gets or sets the template to be used to define each sortable item in the list. /// Use the @context parameter to access the item and its properties. @@ -91,9 +100,6 @@ public partial class FluentSortableList : FluentComponentBase private string Filter => Items.Any(GetItemFiltered) ? ".filtered" : string.Empty; - [Inject] - private IJSRuntime JSRuntime { get; set; } = default!; - protected override async Task OnAfterRenderAsync(bool firstRender) { try @@ -101,7 +107,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (firstRender) { _selfReference = DotNetObjectReference.Create(this); - IJSObjectReference? module = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + IJSObjectReference? module = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await module.InvokeAsync("init", Element, Group, Clone ? "clone" : null, Drop, Sort, Handle ? ".sortable-grab" : null, Filter, Fallback, _selfReference); } } diff --git a/src/Core/Components/Splitter/FluentMultiSplitter.razor.cs b/src/Core/Components/Splitter/FluentMultiSplitter.razor.cs index df37ce7601..08afd1ef95 100644 --- a/src/Core/Components/Splitter/FluentMultiSplitter.razor.cs +++ b/src/Core/Components/Splitter/FluentMultiSplitter.razor.cs @@ -5,6 +5,7 @@ using System.Globalization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -15,6 +16,17 @@ public partial class FluentMultiSplitter : FluentComponentBase private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Splitter/FluentMultiSplitter.razor.js"; private DotNetObjectReference? _objRef = null; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + + /// + [Inject] + private IJSRuntime JS { get; set; } = default!; + + /// + private IJSObjectReference Module { get; set; } = default!; + internal List Panes { get; } = new(); /// @@ -82,13 +94,6 @@ public partial class FluentMultiSplitter : FluentComponentBase .AddStyle("--fluent-multi-splitter-bar-size", BarSize, () => !string.IsNullOrEmpty(BarSize)) .Build(); - /// - [Inject] - private IJSRuntime JS { get; set; } = default!; - - /// - private IJSObjectReference Module { get; set; } = default!; - /// /// Adds the pane. /// @@ -335,7 +340,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - Module = await JS.InvokeAsync("import", JAVASCRIPT_FILE); + Module = await JS.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); _objRef = DotNetObjectReference.Create(this); } } diff --git a/src/Core/Components/Tabs/FluentTab.razor.cs b/src/Core/Components/Tabs/FluentTab.razor.cs index da611a65f9..b3dc049763 100644 --- a/src/Core/Components/Tabs/FluentTab.razor.cs +++ b/src/Core/Components/Tabs/FluentTab.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -6,6 +7,7 @@ namespace Microsoft.FluentUI.AspNetCore.Components; public partial class FluentTab : FluentComponentBase { + private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Tabs/FluentTab.razor.js"; private DotNetObjectReference? _dotNetHelper = null; private IJSObjectReference _jsModule = default!; @@ -18,6 +20,10 @@ public partial class FluentTab : FluentComponentBase .AddStyle("height", $"calc({Owner?.Height} - 40px); overflow-y: auto", () => !string.IsNullOrEmpty(Owner?.Height)) .Build(); + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -131,8 +137,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - _jsModule = await JSRuntime.InvokeAsync("import", - "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Tabs/FluentTab.razor.js"); + _jsModule = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); _dotNetHelper = DotNetObjectReference.Create(this); await _jsModule.InvokeVoidAsync("TabEditable_Changed", _dotNetHelper, $"#{Id} span[contenteditable='true']", Id); diff --git a/src/Core/Components/Tabs/FluentTabs.razor.cs b/src/Core/Components/Tabs/FluentTabs.razor.cs index eb376395eb..92034b9400 100644 --- a/src/Core/Components/Tabs/FluentTabs.razor.cs +++ b/src/Core/Components/Tabs/FluentTabs.razor.cs @@ -3,6 +3,7 @@ // ------------------------------------------------------------------------ using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; using System.Diagnostics.CodeAnalysis; @@ -12,6 +13,8 @@ namespace Microsoft.FluentUI.AspNetCore.Components; public partial class FluentTabs : FluentComponentBase { + private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Overflow/FluentOverflow.razor.js"; + private const string FLUENT_TAB_TAG = "fluent-tab"; private readonly Dictionary _tabs = []; //private string _activeId = string.Empty; @@ -39,6 +42,10 @@ public partial class FluentTabs : FluentComponentBase .AddStyle("display", "none", () => !TabsOverflow.Any()) .Build(); + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -144,8 +151,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { _dotNetHelper = DotNetObjectReference.Create(this); // Overflow - _jsModuleOverflow = await JSRuntime.InvokeAsync("import", - "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Overflow/FluentOverflow.razor.js"); + _jsModuleOverflow = await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); var horizontal = Orientation == Orientation.Horizontal; await _jsModuleOverflow.InvokeVoidAsync("fluentOverflowInitialize", _dotNetHelper, Id, horizontal, FLUENT_TAB_TAG); diff --git a/src/Core/Components/TextField/FluentTextField.razor.cs b/src/Core/Components/TextField/FluentTextField.razor.cs index 860f8b7d7d..ae44adf6f8 100644 --- a/src/Core/Components/TextField/FluentTextField.razor.cs +++ b/src/Core/Components/TextField/FluentTextField.razor.cs @@ -9,6 +9,10 @@ public partial class FluentTextField : FluentInputBase { private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/TextField/FluentTextField.razor.js"; + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + /// [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -98,20 +102,20 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (AutoComplete != null && !string.IsNullOrEmpty(Id)) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("setControlAttribute", Id, "autocomplete", AutoComplete); } if (InputMode != null && !string.IsNullOrEmpty(Id)) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("setControlAttribute", Id, "inputmode", InputMode.ToAttributeValue()); } } if (DataList != null && !string.IsNullOrEmpty(Id)) { - Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("setDataList", Id, DataList); } diff --git a/src/Core/Components/Tooltip/FluentTooltip.razor.cs b/src/Core/Components/Tooltip/FluentTooltip.razor.cs index 79a67a50c1..93ddb78d39 100644 --- a/src/Core/Components/Tooltip/FluentTooltip.razor.cs +++ b/src/Core/Components/Tooltip/FluentTooltip.razor.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.Extensions.DependencyInjection; using Microsoft.FluentUI.AspNetCore.Components.Components.Tooltip; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -17,7 +18,7 @@ public partial class FluentTooltip : FluentComponentBase, IDisposable private IJSRuntime JSRuntime { get; set; } = default!; [Inject] - private LibraryConfiguration? LibraryConfiguration { get; set; } + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; /// /// Gets or sets a reference to the list of registered services. @@ -158,7 +159,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && !string.IsNullOrEmpty(Anchor) && HideTooltipOnCursorLeave == true) { - JSModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + JSModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await JSModule.InvokeVoidAsync("tooltipHideOnCursorLeave", Anchor); } } diff --git a/src/Core/DesignTokens/DesignToken.razor.cs b/src/Core/DesignTokens/DesignToken.razor.cs index 2b4d03ff4d..b06b05e2d2 100644 --- a/src/Core/DesignTokens/DesignToken.razor.cs +++ b/src/Core/DesignTokens/DesignToken.razor.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Extensions; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components.DesignTokens; @@ -11,6 +12,10 @@ public partial class DesignToken : ComponentBase, IDesignToken, IAsyncDisp private Reference Target { get; set; } = new(); + /// + [Inject] + private LibraryConfiguration LibraryConfiguration { get; set; } = default!; + [Inject] private IJSRuntime JSRuntime { get; set; } = default!; @@ -68,7 +73,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) private async Task InitJSReferenceAsync() { - _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE); + _jsModule ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); } #pragma warning disable VSTHRD200 // Use "Async" suffix for async methods diff --git a/src/Core/Extensions/UrlFormatterExtensions.cs b/src/Core/Extensions/UrlFormatterExtensions.cs new file mode 100644 index 0000000000..a233734c2d --- /dev/null +++ b/src/Core/Extensions/UrlFormatterExtensions.cs @@ -0,0 +1,17 @@ +namespace Microsoft.FluentUI.AspNetCore.Components.Extensions; + +internal static class UrlFormatterExtensions +{ + internal static string FormatCollocatedUrl(this string url, LibraryConfiguration configuration) + { + if (configuration.CollocatedJavaScriptQueryString == null) + { + return url; + } + + var queryString = configuration.CollocatedJavaScriptQueryString(url); + + return string.IsNullOrEmpty(queryString) ? url : $"{url}?{queryString}"; + } + +} diff --git a/src/Core/Infrastructure/LibraryConfiguration.cs b/src/Core/Infrastructure/LibraryConfiguration.cs index 6547a2b168..f039f0a921 100644 --- a/src/Core/Infrastructure/LibraryConfiguration.cs +++ b/src/Core/Infrastructure/LibraryConfiguration.cs @@ -9,6 +9,11 @@ namespace Microsoft.FluentUI.AspNetCore.Components; /// public class LibraryConfiguration { + /// + /// Gets the assembly version formatted as a string. + /// + internal static readonly string? AssemblyVersion = typeof(LibraryConfiguration).Assembly.GetName().Version?.ToString(); + /// /// Gets or sets a value indicating whether the library should use the TooltipServiceProvider. /// If set to true, add the FluentTooltipProvider component at end of the MainLayout.razor page. @@ -40,7 +45,21 @@ public bool ValidateClassNames set => Utilities.CssBuilder.ValidateClassNames = value; } + /// + /// Gets or sets the function that formats the URL of the collocated JavaScript file, + /// adding the return value as a query string parameter. + /// By default, the function adds a query string parameter with the version of the assembly: `v=[AssemblyVersion]`. + /// + public Func? CollocatedJavaScriptQueryString { get; set; } = (url) + => string.IsNullOrEmpty(AssemblyVersion) ? string.Empty : $"v={AssemblyVersion}"; + public LibraryConfiguration() { } + + /// + internal static LibraryConfiguration ForUnitTests => new() + { + CollocatedJavaScriptQueryString = null, + }; } diff --git a/tests/Core/Anchor/FluentAnchorTests.cs b/tests/Core/Anchor/FluentAnchorTests.cs index 7efae88f7e..88d868038e 100644 --- a/tests/Core/Anchor/FluentAnchorTests.cs +++ b/tests/Core/Anchor/FluentAnchorTests.cs @@ -1,5 +1,6 @@ using Bunit; using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.FluentUI.AspNetCore.Components.Tests.Anchor; @@ -11,6 +12,7 @@ public partial class FluentAnchorTests : TestContext public FluentAnchorTests() { JSInterop.SetupModule(FluentAnchorRazorJs); + Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/AnchoredRegion/FluentAnchoredRegionTests.cs b/tests/Core/AnchoredRegion/FluentAnchoredRegionTests.cs index cd95f10bfc..a2eb4d405e 100644 --- a/tests/Core/AnchoredRegion/FluentAnchoredRegionTests.cs +++ b/tests/Core/AnchoredRegion/FluentAnchoredRegionTests.cs @@ -1,4 +1,5 @@ using Bunit; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.FluentUI.AspNetCore.Components.Tests.AnchoredRegion; @@ -8,6 +9,7 @@ public class FluentAnchoredRegionTests : TestBase public FluentAnchoredRegionTests() { TestContext.JSInterop.Mode = JSRuntimeMode.Loose; + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/AppBar/FluentAppBarItemTests.razor b/tests/Core/AppBar/FluentAppBarItemTests.razor index 096f5e0cad..34499effeb 100644 --- a/tests/Core/AppBar/FluentAppBarItemTests.razor +++ b/tests/Core/AppBar/FluentAppBarItemTests.razor @@ -13,6 +13,7 @@ JSInterop.Mode = JSRuntimeMode.Loose; Services.AddSingleton(GlobalState); + Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/AppBar/FluentAppBarTests.razor b/tests/Core/AppBar/FluentAppBarTests.razor index cdf97ed80f..d35d6b0782 100644 --- a/tests/Core/AppBar/FluentAppBarTests.razor +++ b/tests/Core/AppBar/FluentAppBarTests.razor @@ -12,6 +12,7 @@ { JSInterop.Mode = JSRuntimeMode.Loose; Services.AddSingleton(GlobalState); + Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/Button/FluentButtonTests.cs b/tests/Core/Button/FluentButtonTests.cs index 2b547f0fc4..f2b165df00 100644 --- a/tests/Core/Button/FluentButtonTests.cs +++ b/tests/Core/Button/FluentButtonTests.cs @@ -1,5 +1,6 @@ using Bunit; using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; using Microsoft.FluentUI.AspNetCore.Components.Tests.Extensions; using Xunit; @@ -7,11 +8,10 @@ namespace Microsoft.FluentUI.AspNetCore.Components.Tests.Button; public partial class FluentButtonTests : TestContext { - private static TestContext TestContext => new(); // TODO: To remove and to use the `RenderComponent` inherited method. - public FluentButtonTests() { - TestContext.JSInterop.Mode = JSRuntimeMode.Loose; + JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] @@ -21,7 +21,7 @@ public void FluentButton_Default() using var id = Identifier.SequentialContext(); // Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.AddChildContent("fluent-button"); }); @@ -34,7 +34,7 @@ public void FluentButton_Default() public void FluentButton_AutofocusAttribute() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.AddChildContent("fluent-button"); parameters.Add(p => p.Autofocus, true); @@ -52,7 +52,7 @@ public void FluentButton_AutofocusAttribute() public void FluentButton_FormIdAttribute(string? formId) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.FormId, formId); parameters.AddChildContent("fluent-button"); @@ -70,7 +70,7 @@ public void FluentButton_FormIdAttribute(string? formId) public void FluentButton_FormActionAttribute(string? formAction) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Action, formAction); parameters.AddChildContent("fluent-button"); @@ -88,7 +88,7 @@ public void FluentButton_FormActionAttribute(string? formAction) public void FluentButton_FormEnctypeAttribute(string? formEnctype) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Enctype, formEnctype); parameters.AddChildContent("fluent-button"); @@ -106,7 +106,7 @@ public void FluentButton_FormEnctypeAttribute(string? formEnctype) public void FluentButton_FormMethodAttribute(string? formMethod) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Method, formMethod); parameters.AddChildContent("fluent-button"); @@ -120,7 +120,7 @@ public void FluentButton_FormMethodAttribute(string? formMethod) public void FluentButton_FormNovalidateAttribute() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.NoValidate, true); parameters.AddChildContent("fluent-button"); @@ -139,7 +139,7 @@ public void FluentButton_FormNovalidateAttribute() public void FluentButton_FormTargetAttribute(string? formTarget) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Target, formTarget); parameters.AddChildContent("fluent-button"); @@ -157,7 +157,7 @@ public void Throw_ArgumentException_When_FormTargetAttribute_IsInvalid(string? f // Arrange && Act Action action = () => { - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Target, formTarget); parameters.AddChildContent("fluent-button"); @@ -175,7 +175,7 @@ public void Throw_ArgumentException_When_FormTargetAttribute_IsInvalid(string? f public void FluentButton_TypeAttribute(ButtonType buttonType) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Type, buttonType); parameters.AddChildContent("fluent-button"); @@ -193,7 +193,7 @@ public void FluentButton_TypeAttribute(ButtonType buttonType) public void FluentButton_IdAttribute(string? id) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Id, id); parameters.AddChildContent("fluent-button"); @@ -211,7 +211,7 @@ public void FluentButton_IdAttribute(string? id) public void FluentButton_ValueAttribute(string? value) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Value, value); parameters.AddChildContent("fluent-button"); @@ -229,7 +229,7 @@ public void FluentButton_ValueAttribute(string? value) public void FluentButton_CurrentValueAttribute(string? currentValue) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.CurrentValue, currentValue); parameters.AddChildContent("fluent-button"); @@ -243,7 +243,7 @@ public void FluentButton_CurrentValueAttribute(string? currentValue) public void FluentButton_DisabledAttribute() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Disabled, true); parameters.AddChildContent("fluent-button"); @@ -257,7 +257,7 @@ public void FluentButton_DisabledAttribute() public void FluentButton_NameAttribute() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Name, "name-value"); parameters.AddChildContent("fluent-button"); @@ -271,7 +271,7 @@ public void FluentButton_NameAttribute() public void FluentButton_RequiredAttribute() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Required, true); parameters.AddChildContent("fluent-button"); @@ -290,7 +290,7 @@ public void FluentButton_RequiredAttribute() public void FluentButton_AppearanceAttribute(Appearance appearance) { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Appearance, appearance); parameters.AddChildContent("fluent-button"); @@ -304,7 +304,7 @@ public void FluentButton_AppearanceAttribute(Appearance appearance) public void FluentButton_ClassAttribute() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Class, "additional-class"); parameters.AddChildContent("fluent-button"); @@ -318,7 +318,7 @@ public void FluentButton_ClassAttribute() public void FluentButton_StyleAttribute() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Style, "background-color: green;"); parameters.AddChildContent("fluent-button"); @@ -332,7 +332,7 @@ public void FluentButton_StyleAttribute() public void FluentButton_AdditionalAttribute() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.AddUnmatched("additional-attribute-name", "additional-attribute-value"); parameters.AddChildContent("fluent-button"); @@ -346,7 +346,7 @@ public void FluentButton_AdditionalAttribute() public void FluentButton_AdditionalAttributes() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.AddUnmatched("additional-attribute1-name", "additional-attribute1-value"); parameters.AddUnmatched("additional-attribute2-name", "additional-attribute2-value"); @@ -361,7 +361,7 @@ public void FluentButton_AdditionalAttributes() public void FluentButton_IconStart() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.IconStart, SampleIcons.Info); parameters.AddChildContent("My button"); @@ -375,7 +375,7 @@ public void FluentButton_IconStart() public void FluentButton_IconEnd() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.IconEnd, SampleIcons.Info); parameters.AddChildContent("My button"); @@ -389,7 +389,7 @@ public void FluentButton_IconEnd() public void FluentButton_IconNoContent() { // Arrange && Act - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.IconEnd, SampleIcons.Info); }); @@ -404,7 +404,7 @@ public void FluentButton_OnClick_Disabled() var clicked = false; // Arrange - var cut = TestContext.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.OnClick, (e) => { clicked = true; }); parameters.AddChildContent("My button"); diff --git a/tests/Core/Checkbox/FluentCheckboxTests.cs b/tests/Core/Checkbox/FluentCheckboxTests.cs index 69df3693e3..8e040bc426 100644 --- a/tests/Core/Checkbox/FluentCheckboxTests.cs +++ b/tests/Core/Checkbox/FluentCheckboxTests.cs @@ -1,10 +1,16 @@ using Bunit; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.FluentUI.AspNetCore.Components.Tests.Checkbox; public class FluentCheckboxTests : TestBase { + public FluentCheckboxTests() + { + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/tests/Core/Checkbox/FluentCheckboxThreeStatesTests.razor b/tests/Core/Checkbox/FluentCheckboxThreeStatesTests.razor index 7b55a68e53..e59058f3fa 100644 --- a/tests/Core/Checkbox/FluentCheckboxThreeStatesTests.razor +++ b/tests/Core/Checkbox/FluentCheckboxThreeStatesTests.razor @@ -1,15 +1,19 @@ -@using Xunit; +@using Xunit; @inherits TestContext @code { + public FluentCheckboxThreeStatesTests() + { + JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); + } + [Theory] [InlineData(false, true, true)] // Unchecked => Checked [InlineData(true, null, false)] // Checked => Indeterminate [InlineData(null, false, false)] // Indeterminate => Unchecked public void FluentCheckbox_ThreeStates(bool? initialState, bool? assertState, bool assertValue) { - this.JSInterop.Mode = JSRuntimeMode.Loose; - bool myValue = initialState ?? false; bool? myState = initialState; @@ -33,8 +37,6 @@ [InlineData(true, false, false)] // Checked => Unchecked public void FluentCheckbox_ThreeStates_InverseOrder(bool? initialState, bool? assertState, bool assertValue) { - this.JSInterop.Mode = JSRuntimeMode.Loose; - bool myValue = initialState ?? false; bool? myState = initialState; @@ -56,8 +58,6 @@ [Fact] public void FluentCheckbox_ThreeStates_ShowIntermediateFalse() { - this.JSInterop.Mode = JSRuntimeMode.Loose; - // Start Intermediate bool? myState = null; @@ -86,8 +86,6 @@ [InlineData(false, true)] // Checked => Unchecked public void FluentCheckbox_ThreeStatesFalse(bool initialValue, bool assertValue) { - this.JSInterop.Mode = JSRuntimeMode.Loose; - bool myValue = initialValue; // Arrange && Act @@ -106,12 +104,9 @@ [Fact] public void FluentCheckbox_Labels() { - this.JSInterop.Mode = JSRuntimeMode.Loose; - - // Arrange var cut = Render(@My customized content); cut.Verify(); } -} \ No newline at end of file +} diff --git a/tests/Core/DataGrid/DataGridSortByTests.razor b/tests/Core/DataGrid/DataGridSortByTests.razor index a7798c7063..34bbaaf837 100644 --- a/tests/Core/DataGrid/DataGridSortByTests.razor +++ b/tests/Core/DataGrid/DataGridSortByTests.razor @@ -9,6 +9,7 @@ public DataGridSortByTests() { JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); // Register Service var keycodeService = new KeyCodeService(); diff --git a/tests/Core/DataGrid/FluentDataGridColumSelectTests.razor.cs b/tests/Core/DataGrid/FluentDataGridColumSelectTests.razor.cs index 42f2901c4d..5c866db616 100644 --- a/tests/Core/DataGrid/FluentDataGridColumSelectTests.razor.cs +++ b/tests/Core/DataGrid/FluentDataGridColumSelectTests.razor.cs @@ -12,6 +12,7 @@ public partial class FluentDataGridColumSelectTests public FluentDataGridColumSelectTests() { JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); // Register Service var keycodeService = new KeyCodeService(); diff --git a/tests/Core/Divider/FluentDividerTests.razor b/tests/Core/Divider/FluentDividerTests.razor index 8eda80ab1f..fc43f8a25c 100644 --- a/tests/Core/Divider/FluentDividerTests.razor +++ b/tests/Core/Divider/FluentDividerTests.razor @@ -1,13 +1,16 @@ -@using Xunit; +@using Xunit; @inherits TestContext @code { - [Fact] - public void FluentDivider_Default() + public FluentDividerTests() { - // Arrange JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); + } + [Fact] + public void FluentDivider_Default() + { // Act var cut = Render(@); @@ -18,9 +21,6 @@ [Fact] public void FluentDivider_Separator() { - // Arrange - JSInterop.Mode = JSRuntimeMode.Loose; - // Act var cut = Render(@); @@ -35,13 +35,10 @@ [InlineData(DividerRole.Presentation, Orientation.Vertical)] public void FluentDivider_RoleOrientation(DividerRole role, Orientation orientation) { - // Arrange - JSInterop.Mode = JSRuntimeMode.Loose; - // Act var cut = Render(@My content); // Assert cut.Verify(memberName: $"{nameof(FluentDivider_RoleOrientation)}_{role}_{orientation}"); } -} \ No newline at end of file +} diff --git a/tests/Core/Grid/FluentGridTests.razor b/tests/Core/Grid/FluentGridTests.razor index 88c4da259b..1ba4cb7a30 100644 --- a/tests/Core/Grid/FluentGridTests.razor +++ b/tests/Core/Grid/FluentGridTests.razor @@ -4,7 +4,8 @@ { public FluentGridTests() { - this.JSInterop.Mode = JSRuntimeMode.Loose; + JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/Icons/FluentIconTests.razor b/tests/Core/Icons/FluentIconTests.razor index 200cebcf8a..d7e554a577 100644 --- a/tests/Core/Icons/FluentIconTests.razor +++ b/tests/Core/Icons/FluentIconTests.razor @@ -3,6 +3,11 @@ @inherits TestContext @code { + public FluentIconTests() + { + Services.AddSingleton(LibraryConfiguration.ForUnitTests); + } + [Fact] public void FluentIcon_Default() { diff --git a/tests/Core/InputFile/FluentInputFileTests.razor b/tests/Core/InputFile/FluentInputFileTests.razor index 645e4c23f5..e0f3ea3f2f 100644 --- a/tests/Core/InputFile/FluentInputFileTests.razor +++ b/tests/Core/InputFile/FluentInputFileTests.razor @@ -1,7 +1,12 @@ -@using Xunit; +@using Xunit; @inherits TestContext @code { + public FluentInputFileTests() + { + Services.AddSingleton(LibraryConfiguration.ForUnitTests); + } + [Fact] public void FluentInputFile_Default() { diff --git a/tests/Core/KeyCodeProvider/FluentKeyCodeTests.razor b/tests/Core/KeyCodeProvider/FluentKeyCodeTests.razor index a2c57e166e..d76d026f51 100644 --- a/tests/Core/KeyCodeProvider/FluentKeyCodeTests.razor +++ b/tests/Core/KeyCodeProvider/FluentKeyCodeTests.razor @@ -2,11 +2,15 @@ @inherits TestContext @code { - [Fact] - public async Task FluentKeyCode_KeyA() + public FluentKeyCodeTests() { JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); + } + [Fact] + public async Task FluentKeyCode_KeyA() + { FluentKeyCodeEventArgs pressed = new(); // Arrange && Act @@ -28,8 +32,6 @@ [Fact] public async Task FluentKeyCode_KeyDown_KeyA() { - JSInterop.Mode = JSRuntimeMode.Loose; - FluentKeyCodeEventArgs pressed = new(); // Arrange && Act @@ -44,8 +46,6 @@ [Fact] public async Task FluentKeyCode_ChildContent() { - JSInterop.Mode = JSRuntimeMode.Loose; - FluentKeyCodeEventArgs pressed = new(); // Arrange && Act @@ -67,8 +67,6 @@ [Fact] public async Task FluentKeyCode_CtrlShiftAltMeta() { - JSInterop.Mode = JSRuntimeMode.Loose; - FluentKeyCodeEventArgs pressed = new(); // Arrange && Act @@ -96,8 +94,6 @@ [InlineData(true, true, true, true, "Ctrl + Shift + Alt + Meta + A")] public async Task FluentKeyCode_KeyA_ToString(bool ctrlKey, bool shiftKey, bool altKey, bool metaKey, string expected) { - JSInterop.Mode = JSRuntimeMode.Loose; - FluentKeyCodeEventArgs pressed = new(); // Arrange && Act @@ -112,7 +108,6 @@ [Fact] public async Task FluentKeyCode_Provider_Function() { - JSInterop.Mode = JSRuntimeMode.Loose; FluentKeyCodeEventArgs pressed = new(); // Register Service @@ -146,7 +141,6 @@ [Fact] public async Task FluentKeyCode_Provider_Interface() { - JSInterop.Mode = JSRuntimeMode.Loose; FluentKeyCodeEventArgs pressed = new(); // Register Service @@ -177,8 +171,6 @@ [Fact] public async Task FluentKeyCode_AnchorOrChildContent_Required() { - JSInterop.Mode = JSRuntimeMode.Loose; - FluentKeyCodeEventArgs pressed = new(); // Arrange && Act diff --git a/tests/Core/NavMenu/FluentNavGroupTests.cs b/tests/Core/NavMenu/FluentNavGroupTests.cs index 2fa7cda68b..8cb13cbb69 100644 --- a/tests/Core/NavMenu/FluentNavGroupTests.cs +++ b/tests/Core/NavMenu/FluentNavGroupTests.cs @@ -1,4 +1,5 @@ using Bunit; +using Microsoft.Extensions.DependencyInjection; using Microsoft.FluentUI.AspNetCore.Components.Tests.Extensions; using Xunit; @@ -9,6 +10,7 @@ public class FluentNavGroupTests : TestBase public FluentNavGroupTests() { TestContext.JSInterop.Mode = JSRuntimeMode.Loose; + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/NavMenu/FluentNavMenuTests.cs b/tests/Core/NavMenu/FluentNavMenuTests.cs index 93f0a7fb30..ac29ed0a2e 100644 --- a/tests/Core/NavMenu/FluentNavMenuTests.cs +++ b/tests/Core/NavMenu/FluentNavMenuTests.cs @@ -1,4 +1,5 @@ using Bunit; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.FluentUI.AspNetCore.Components.Tests.NavMenu; @@ -8,6 +9,7 @@ public class FluentNavMenuTests : TestBase public FluentNavMenuTests() { TestContext.JSInterop.Mode = JSRuntimeMode.Loose; + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/ProfileMenu/FluentProfileMenuTests.razor b/tests/Core/ProfileMenu/FluentProfileMenuTests.razor index baedcc8ced..5b38beb057 100644 --- a/tests/Core/ProfileMenu/FluentProfileMenuTests.razor +++ b/tests/Core/ProfileMenu/FluentProfileMenuTests.razor @@ -7,6 +7,7 @@ public FluentProfileMenuTests() { this.Services.AddScoped(); + this.Services.AddSingleton(LibraryConfiguration.ForUnitTests); this.JSInterop.Mode = JSRuntimeMode.Loose; } diff --git a/tests/Core/PullToRefresh/FluentPullToRefreshTests.razor b/tests/Core/PullToRefresh/FluentPullToRefreshTests.razor index a36805e764..56e36e2bb7 100644 --- a/tests/Core/PullToRefresh/FluentPullToRefreshTests.razor +++ b/tests/Core/PullToRefresh/FluentPullToRefreshTests.razor @@ -5,6 +5,7 @@ public FluentPullToRefreshTests() { this.JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); } // write some unit tests here diff --git a/tests/Core/Slider/FluentSliderLaberTests.cs b/tests/Core/Slider/FluentSliderLaberTests.cs index e1c3232d16..661ede5fd5 100644 --- a/tests/Core/Slider/FluentSliderLaberTests.cs +++ b/tests/Core/Slider/FluentSliderLaberTests.cs @@ -1,6 +1,7 @@ namespace Microsoft.FluentUI.AspNetCore.Components.Tests.Slider; using Bunit; +using Microsoft.Extensions.DependencyInjection; using Xunit; public class FluentSliderLabelTests : TestBase @@ -8,6 +9,7 @@ public class FluentSliderLabelTests : TestBase public FluentSliderLabelTests() { TestContext.JSInterop.Mode = JSRuntimeMode.Loose; + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/SortableList/FluentSortableListTests.razor b/tests/Core/SortableList/FluentSortableListTests.razor index 14c623a166..006ffb587b 100644 --- a/tests/Core/SortableList/FluentSortableListTests.razor +++ b/tests/Core/SortableList/FluentSortableListTests.razor @@ -7,7 +7,6 @@ { public int Id { get; set; } public string Name { get; set; } = ""; - public bool Disabled { get; set; } = false; } @@ -16,8 +15,7 @@ public FluentSortableListTests() { JSInterop.Mode = JSRuntimeMode.Loose; - //JSInterop.SetupModule("./_content/Microsoft.FluentUI.AspNetCore.Components/Components/SortableList/FluentSortableList.razor.js"); - //JSInterop.Setup("init", _ => true); + Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/Splitter/FluentMultiSplitterTests.razor b/tests/Core/Splitter/FluentMultiSplitterTests.razor index 030899e312..fde29441a5 100644 --- a/tests/Core/Splitter/FluentMultiSplitterTests.razor +++ b/tests/Core/Splitter/FluentMultiSplitterTests.razor @@ -3,14 +3,17 @@ @code { + public FluentMultiSplitterTests() + { + JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddSingleton(LibraryConfiguration.ForUnitTests); + } + [Fact] public void FluentMultiSplitter_Horizontal_Basic() { // Arrange - using var ctx = new Bunit.TestContext(); - ctx.JSInterop.Mode = JSRuntimeMode.Loose; - - var cut = ctx.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Orientation, Orientation.Horizontal); parameters.AddChildContent(pane => @@ -31,10 +34,7 @@ public void FluentMultiSplitter_Vertical_Basic() { // Arrange - using var ctx = new Bunit.TestContext(); - ctx.JSInterop.Mode = JSRuntimeMode.Loose; - - var cut = ctx.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.Orientation, Orientation.Vertical); parameters.AddChildContent(pane => @@ -55,10 +55,7 @@ public void FluentMultiSplitter_PaneCollapsible() { // Arrange - using var ctx = new Bunit.TestContext(); - ctx.JSInterop.Mode = JSRuntimeMode.Loose; - - var cut = ctx.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.AddChildContent(pane => { @@ -82,10 +79,7 @@ bool eventCalled = false; // Arrange - using var ctx = new Bunit.TestContext(); - ctx.JSInterop.Mode = JSRuntimeMode.Loose; - - var cut = ctx.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.OnResize, (e) => eventCalled = true); parameters.AddChildContent(pane => @@ -110,10 +104,7 @@ public void FluentMultiSplitter_PaneResizedReceived() { // Arrange - using var ctx = new Bunit.TestContext(); - ctx.JSInterop.Mode = JSRuntimeMode.Loose; - - var cut = ctx.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.AddChildContent(pane => { @@ -139,10 +130,7 @@ public async Task FluentMultiSplitter_PaneResizeCanceled() { // Arrange - using var ctx = new Bunit.TestContext(); - ctx.JSInterop.Mode = JSRuntimeMode.Loose; - - var cut = ctx.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.OnResize, (e) => e.Cancel = true); parameters.AddChildContent(pane => @@ -172,8 +160,6 @@ public void FluentMultiSplitter_Size_Fixed() { // Arrange - JSInterop.Mode = JSRuntimeMode.Loose; - var cut = Render( @ Pane A @@ -201,10 +187,7 @@ bool eventCalled = false; // Arrange - using var ctx = new Bunit.TestContext(); - ctx.JSInterop.Mode = JSRuntimeMode.Loose; - - var cut = ctx.RenderComponent(parameters => + var cut = RenderComponent(parameters => { parameters.Add(p => p.OnExpand, (e) => eventCalled = true); parameters.Add(p => p.OnCollapse, (e) => eventCalled = true); diff --git a/tests/Core/Utilities/JSModuleTests.cs b/tests/Core/Utilities/JSModuleTests.cs deleted file mode 100644 index caee9adceb..0000000000 --- a/tests/Core/Utilities/JSModuleTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -namespace Microsoft.FluentUI.AspNetCore.Components.Tests.Utilities; - -public class JSModuleTests -{ - //[Fact] - //public async Task InvokeVoidAsync_InvokesMethod() - //{ - // // Arrange - // var jsRuntime = new MockJSRuntime(); - // var module = new TestJSModule(jsRuntime, "test.js"); - - // // Act - // await module.InvokeVoidAsync("testMethod"); - - // // Assert - // Assert.True(jsRuntime.InvokedMethods.Contains(("import", new object[] { "test.js" }))); - // Assert.True(jsRuntime.InvokedMethods.Contains(("testMethod", null))); - //} - - //[Fact] - //public async Task InvokeAsync_InvokesMethod() - //{ - // // Arrange - // var jsRuntime = new MockJSRuntime(); - // var module = new TestJSModule(jsRuntime, "test.js"); - - // // Act - // var result = await module.InvokeAsync("testMethod"); - - // // Assert - // Assert.True(jsRuntime.InvokedMethods.Contains(("import", new object[] { "test.js" }))); - // Assert.True(jsRuntime.InvokedMethods.Contains(("testMethod", null))); - // Assert.Equal("testResult", result); - //} - - //[Fact] - //public async Task DisposeAsync_DisposesModule() - //{ - // // Arrange - // var jsRuntime = new MockJSRuntime(); - // var module = new TestJSModule(jsRuntime, "test.js"); - - // // Act - // await module.DisposeAsync(); - - // // Assert - // Assert.True(jsRuntime.InvokedMethods.Contains(("import", new object[] { "test.js" }))); - // Assert.True(jsRuntime.InvokedMethods.Contains(("dispose", null))); - //} - - //private class TestJSModule : JSModule - //{ - // public TestJSModule(IJSRuntime js, string moduleUrl) : base(js, moduleUrl) - // { - // } - - // public async Task InvokeVoidAsync(string method) - // { - // await Task.Delay(100); - - // } - - // public new ValueTask DisposeAsync() - // { - // return base.DisposeAsync(); - // } - //} - - //public class MockJSRuntime : IJSRuntime - //{ - // public ValueTask InvokeAsync(string identifier, object?[]? args) - // { - // return default; - // } - - // public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args) - // { - // return default; - // } - //} -} diff --git a/tests/Core/_ToDo/HorizontalScroll/FluentHorizontalScrollTests.cs b/tests/Core/_ToDo/HorizontalScroll/FluentHorizontalScrollTests.cs index c9ee5d1738..33db9c4702 100644 --- a/tests/Core/_ToDo/HorizontalScroll/FluentHorizontalScrollTests.cs +++ b/tests/Core/_ToDo/HorizontalScroll/FluentHorizontalScrollTests.cs @@ -1,4 +1,5 @@ using Bunit; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.FluentUI.AspNetCore.Components.Tests.HorizontalScroll; @@ -7,7 +8,9 @@ public class FluentHorizontalScrollTests : TestBase public FluentHorizontalScrollTests() { TestContext.JSInterop.SetupModule("./_content/Microsoft.FluentUI.AspNetCore.Components/Components/HorizontalScroll/FluentHorizontalScroll.razor.js"); + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); } + [Fact] public void FluentHorizontalScroll_Default() { diff --git a/tests/Core/_ToDo/MainLayout/FluentMainLayoutTests.cs b/tests/Core/_ToDo/MainLayout/FluentMainLayoutTests.cs index 8c7f344cd8..a18a99e611 100644 --- a/tests/Core/_ToDo/MainLayout/FluentMainLayoutTests.cs +++ b/tests/Core/_ToDo/MainLayout/FluentMainLayoutTests.cs @@ -1,4 +1,5 @@ using Bunit; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.FluentUI.AspNetCore.Components.Tests.MainLayout; @@ -7,6 +8,7 @@ public class FluentMainLayoutTests : TestBase public FluentMainLayoutTests() { TestContext.JSInterop.Mode = JSRuntimeMode.Loose; + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/_ToDo/Menu/FluentMenuTests.cs b/tests/Core/_ToDo/Menu/FluentMenuTests.cs index 0a9202b95e..0d70cf6637 100644 --- a/tests/Core/_ToDo/Menu/FluentMenuTests.cs +++ b/tests/Core/_ToDo/Menu/FluentMenuTests.cs @@ -1,4 +1,5 @@ using Bunit; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.FluentUI.AspNetCore.Components.Tests.Menu; @@ -7,6 +8,7 @@ public class FluentMenuTests : TestBase public FluentMenuTests() { TestContext.JSInterop.SetupModule("./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Menu/FluentMenu.razor.js"); + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); } [Fact] diff --git a/tests/Core/_ToDo/MenuButton/FluentMenuButtonTests.cs b/tests/Core/_ToDo/MenuButton/FluentMenuButtonTests.cs index 66ade92692..ac3895b4e4 100644 --- a/tests/Core/_ToDo/MenuButton/FluentMenuButtonTests.cs +++ b/tests/Core/_ToDo/MenuButton/FluentMenuButtonTests.cs @@ -1,9 +1,16 @@ using Bunit; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.FluentUI.AspNetCore.Components.Tests.MenuButton; + public class FluentMenuButtonTests : TestBase { + public FluentMenuButtonTests() + { + TestContext.Services.AddSingleton(LibraryConfiguration.ForUnitTests); + } + [Fact] public void FluentMenuButton_Default() { From 5ebaa3db8562b58a9c4f1246188be5797cf9e2c5 Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Wed, 17 Jul 2024 20:49:57 +0200 Subject: [PATCH 14/22] - Fix #2390 by moving logic inside popover close code (#2393) - Fix #2391 by adding `Fixed` parameter t0 `FluentOverflowItem` Co-authored-by: Denis Voituron --- .../Shared/Microsoft.FluentUI.AspNetCore.Components.xml | 8 +++++++- src/Core/Components/Overflow/FluentOverflowItem.razor | 4 ++-- src/Core/Components/Overflow/FluentOverflowItem.razor.cs | 7 +++++++ src/Core/Components/Popover/FluentPopover.razor.cs | 4 ++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml index 45fdf8cdac..24e0faf34b 100644 --- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml +++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml @@ -7025,6 +7025,12 @@ Gets True if this component is out of panel. + + + Gets or sets if this item dos not participates in overflow logic. + Defaults to false + + Gets the InnerText of . @@ -7219,7 +7225,7 @@ - Gets or sets a value indicating whether the region overlaps the anchor on the horizontal axis. + Gets or sets a value indicating whether the region overlaps the anchor on the horizontal axis. Default is true which places the region aligned with the anchor element. diff --git a/src/Core/Components/Overflow/FluentOverflowItem.razor b/src/Core/Components/Overflow/FluentOverflowItem.razor index 9087fb6d96..bc12287af7 100644 --- a/src/Core/Components/Overflow/FluentOverflowItem.razor +++ b/src/Core/Components/Overflow/FluentOverflowItem.razor @@ -1,6 +1,6 @@ @namespace Microsoft.FluentUI.AspNetCore.Components @inherits FluentComponentBase -
+
@ChildContent -
\ No newline at end of file +
diff --git a/src/Core/Components/Overflow/FluentOverflowItem.razor.cs b/src/Core/Components/Overflow/FluentOverflowItem.razor.cs index 9b6c143cc6..a7fb7dbdbc 100644 --- a/src/Core/Components/Overflow/FluentOverflowItem.razor.cs +++ b/src/Core/Components/Overflow/FluentOverflowItem.razor.cs @@ -34,6 +34,13 @@ public partial class FluentOverflowItem : IDisposable [Parameter] public RenderFragment? ChildContent { get; set; } + /// + /// Gets or sets if this item dos not participates in overflow logic. + /// Defaults to false + /// + [Parameter] + public bool Fixed { get; set; } = false; + /// /// Gets True if this component is out of panel. /// diff --git a/src/Core/Components/Popover/FluentPopover.razor.cs b/src/Core/Components/Popover/FluentPopover.razor.cs index c4d7ab1e8a..30c3f912de 100644 --- a/src/Core/Components/Popover/FluentPopover.razor.cs +++ b/src/Core/Components/Popover/FluentPopover.razor.cs @@ -27,7 +27,7 @@ public partial class FluentPopover : FluentComponentBase public HorizontalPosition? HorizontalPosition { get; set; } = AspNetCore.Components.HorizontalPosition.Unset; /// - /// Gets or sets a value indicating whether the region overlaps the anchor on the horizontal axis. + /// Gets or sets a value indicating whether the region overlaps the anchor on the horizontal axis. /// Default is true which places the region aligned with the anchor element. /// [Parameter] @@ -130,11 +130,11 @@ protected override void OnParametersSet() protected virtual async Task CloseAsync() { Open = false; - await AnchoredRegion.FocusToOriginalElementAsync(); if (OpenChanged.HasDelegate) { await OpenChanged.InvokeAsync(Open); } + await AnchoredRegion.FocusToOriginalElementAsync(); } /// From 3099581754c83beca5a1e35b480a51e9e7a94abf Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Thu, 18 Jul 2024 10:13:07 +0200 Subject: [PATCH 15/22] Fix #2367 by passing through parameters.Modal to `ShowSplashScreen...` methods (#2398) Also adjust the samples to show these behaviors --- .../Microsoft.FluentUI.AspNetCore.Components.xml | 10 +++++----- .../SplashScreen/Examples/CustomSplashScreen.razor | 5 +++-- .../Examples/DialogSplashScreenCustom.razor.cs | 3 ++- .../Examples/DialogSplashScreenDefault.razor.cs | 2 ++ .../Dialog/Services/DialogService-SplashScreen.cs | 4 ++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml index 24e0faf34b..3e0f17a4fb 100644 --- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml +++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml @@ -7020,17 +7020,17 @@ Gets or sets the content to display. All first HTML elements are included in the items flow. - - - Gets True if this component is out of panel. - - Gets or sets if this item dos not participates in overflow logic. Defaults to false + + + Gets True if this component is out of panel. + + Gets the InnerText of . diff --git a/examples/Demo/Shared/Pages/SplashScreen/Examples/CustomSplashScreen.razor b/examples/Demo/Shared/Pages/SplashScreen/Examples/CustomSplashScreen.razor index 8d48779ff9..107bc46dce 100644 --- a/examples/Demo/Shared/Pages/SplashScreen/Examples/CustomSplashScreen.razor +++ b/examples/Demo/Shared/Pages/SplashScreen/Examples/CustomSplashScreen.razor @@ -36,11 +36,12 @@ { if (firstRender) { + // Simulation of loading process - await Task.Delay(7000); + await Task.Delay(Content.DisplayTime); // Close the dialog await Dialog.CloseAsync(); } } -} \ No newline at end of file +} diff --git a/examples/Demo/Shared/Pages/SplashScreen/Examples/DialogSplashScreenCustom.razor.cs b/examples/Demo/Shared/Pages/SplashScreen/Examples/DialogSplashScreenCustom.razor.cs index 1cbeb3f758..98f7625b56 100644 --- a/examples/Demo/Shared/Pages/SplashScreen/Examples/DialogSplashScreenCustom.razor.cs +++ b/examples/Demo/Shared/Pages/SplashScreen/Examples/DialogSplashScreenCustom.razor.cs @@ -17,6 +17,7 @@ private async Task OpenSplashCustomAsync() LoadingText = "Filling the re-useable bottles...", Message = (MarkupString)"Don't drink too much water!", Logo = "_content/FluentUI.Demo.Shared/images/Splash_Corporation_logo.png", + DisplayTime = 7000 }, Width = "500px", Height = "300px", @@ -35,7 +36,7 @@ private async Task OpenSplashCustomAsync() } private void OpenSplashCustom() { - DemoLogger.WriteLine($"Open custom splashscreen for 7 seconds"); + DemoLogger.WriteLine($"Open custom splashscreen for 4 seconds"); DialogParameters parameters = new() { Content = new() diff --git a/examples/Demo/Shared/Pages/SplashScreen/Examples/DialogSplashScreenDefault.razor.cs b/examples/Demo/Shared/Pages/SplashScreen/Examples/DialogSplashScreenDefault.razor.cs index 25ab882b31..57d8be51d3 100644 --- a/examples/Demo/Shared/Pages/SplashScreen/Examples/DialogSplashScreenDefault.razor.cs +++ b/examples/Demo/Shared/Pages/SplashScreen/Examples/DialogSplashScreenDefault.razor.cs @@ -22,6 +22,7 @@ private async Task OpenSplashDefaultAsync() Logo = FluentSplashScreen.LOGO, }, PreventDismissOnOverlayClick = true, + Modal = false, Width = "640px", Height = "480px", }; @@ -57,6 +58,7 @@ private void OpenSplashDefault() }, Width = "640px", Height = "480px", + Modal = true, }; DialogService.ShowSplashScreen(this, HandleDefaultSplashAsync, parameters); } diff --git a/src/Core/Components/Dialog/Services/DialogService-SplashScreen.cs b/src/Core/Components/Dialog/Services/DialogService-SplashScreen.cs index 34ba085f46..0d7797b1e7 100644 --- a/src/Core/Components/Dialog/Services/DialogService-SplashScreen.cs +++ b/src/Core/Components/Dialog/Services/DialogService-SplashScreen.cs @@ -36,7 +36,7 @@ public void ShowSplashScreen(Type component, object receiver, Func ShowSplashScreenAsync(Type component, object { DialogType = DialogType.SplashScreen, Alignment = HorizontalAlignment.Center, - Modal = false, + Modal = parameters.Modal, PreventDismissOnOverlayClick = parameters.PreventDismissOnOverlayClick, ShowDismiss = false, ShowTitle = false, From c61a8e323b4f4ec17733c70aa67a2e26cf5e00b7 Mon Sep 17 00:00:00 2001 From: f4n0 <36199751+f4n0@users.noreply.github.com> Date: Thu, 18 Jul 2024 21:17:35 +0200 Subject: [PATCH 16/22] [DataGrid] Avoid excessive calling of reinitialize method in JavaScript (#2403) --- src/Core/Components/DataGrid/FluentDataGrid.razor.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.js b/src/Core/Components/DataGrid/FluentDataGrid.razor.js index 32e47c79b3..3adef2e243 100644 --- a/src/Core/Components/DataGrid/FluentDataGrid.razor.js +++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.js @@ -89,7 +89,11 @@ export function checkColumnOptionsPosition(gridElement) { } } +let LatestGridElement = null; export function enableColumnResizing(gridElement) { + if (gridElement === LatestGridElement) + return; + LatestGridElement = gridElement; const columns = []; let min = 50; let headerBeingResized; From b9bcf9ddbb53281654e375e22d29db43e371bef9 Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Thu, 18 Jul 2024 21:26:33 +0200 Subject: [PATCH 17/22] After merge PR tweak. place globals at the toip of the file anduse preferred casing --- src/Core/Components/DataGrid/FluentDataGrid.razor.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.js b/src/Core/Components/DataGrid/FluentDataGrid.razor.js index 3adef2e243..e2abc6a2e0 100644 --- a/src/Core/Components/DataGrid/FluentDataGrid.razor.js +++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.js @@ -1,4 +1,6 @@ -let initialColumnsWidths = ''; +var initialColumnsWidths = ''; +var latestGridElement = null; + export function init(gridElement) { if (gridElement === undefined || gridElement === null) { return; @@ -89,11 +91,11 @@ export function checkColumnOptionsPosition(gridElement) { } } -let LatestGridElement = null; + export function enableColumnResizing(gridElement) { - if (gridElement === LatestGridElement) + if (gridElement === latestGridElement) return; - LatestGridElement = gridElement; + latestGridElement = gridElement; const columns = []; let min = 50; let headerBeingResized; From 7782569aeaa716ca76223939e7329bb6745c1149 Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Thu, 18 Jul 2024 22:05:53 +0200 Subject: [PATCH 18/22] [DataGrid] set min column width to 75 (was 50) --- src/Core/Components/DataGrid/FluentDataGrid.razor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.js b/src/Core/Components/DataGrid/FluentDataGrid.razor.js index e2abc6a2e0..d43e3c046b 100644 --- a/src/Core/Components/DataGrid/FluentDataGrid.razor.js +++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.js @@ -97,7 +97,7 @@ export function enableColumnResizing(gridElement) { return; latestGridElement = gridElement; const columns = []; - let min = 50; + let min = 75; let headerBeingResized; let resizeHandle; @@ -115,7 +115,7 @@ export function enableColumnResizing(gridElement) { const width = pointerLocalLeft - headerLocalLeft; const column = columns.find(({ header }) => header === headerBeingResized); - min = header.querySelector('.col-options-button') ? 75 : 50; + min = header.querySelector('.col-options-button') ? 100 : 75; column.size = Math.max(min, width) + 'px'; From 48b5079a60f37800f8c6fba7932c808b0b9cc169 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Thu, 18 Jul 2024 21:10:52 +0100 Subject: [PATCH 19/22] [Docs] Fix typos (#2400) * Fix typo Fix two typos I spotted in #2393. * Fix typo Fix typo in PR template. --------- Co-authored-by: Vincent Baaij --- .github/pull_request_template.md | 4 ++-- .../Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml | 2 +- src/Core/Components/Overflow/FluentOverflowItem.razor.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 579de80c44..52967adec9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -54,9 +54,9 @@ Please provide a summary of the tests affected by this work and any unique strat - [ ] I have added a new component -- [ ] I have added [Unit Tests](https://github.com/Microsoft/fluentui-blazor/blob/master/unit-tests.md) for my new compontent +- [ ] I have added [Unit Tests](https://github.com/Microsoft/fluentui-blazor/blob/master/unit-tests.md) for my new component - [ ] I have modified an existing component -- [ ] I have validated the [Unit Tests](https://github.com/Microsoft/fluentui-blazor/blob/master/unit-tests.md) for an existing component +- [ ] I have validated the [Unit Tests](https://github.com/Microsoft/fluentui-blazor/blob/master/unit-tests.md) for an existing component ## ⏭ Next Steps diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml index 3e0f17a4fb..35a63a5652 100644 --- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml +++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml @@ -7022,7 +7022,7 @@ - Gets or sets if this item dos not participates in overflow logic. + Gets or sets if this item does not participate in overflow logic. Defaults to false diff --git a/src/Core/Components/Overflow/FluentOverflowItem.razor.cs b/src/Core/Components/Overflow/FluentOverflowItem.razor.cs index a7fb7dbdbc..8dddd4208d 100644 --- a/src/Core/Components/Overflow/FluentOverflowItem.razor.cs +++ b/src/Core/Components/Overflow/FluentOverflowItem.razor.cs @@ -35,7 +35,7 @@ public partial class FluentOverflowItem : IDisposable public RenderFragment? ChildContent { get; set; } /// - /// Gets or sets if this item dos not participates in overflow logic. + /// Gets or sets if this item does not participate in overflow logic. /// Defaults to false /// [Parameter] From 42e4823501b306edc9d754f5a97544e99909a27a Mon Sep 17 00:00:00 2001 From: Denis Voituron Date: Thu, 18 Jul 2024 22:17:58 +0200 Subject: [PATCH 20/22] [Overflow] Add Fixed enumeration (#2401) * Add OverflowItemFixed enumeration * Refactor --- ...crosoft.FluentUI.AspNetCore.Components.xml | 22 ++++++++++++- .../Examples/OverflowDefaultExample.razor | 21 +++++++++++++ .../Overflow/FluentOverflow.razor.css | 9 +++++- .../Overflow/FluentOverflowItem.razor | 2 +- .../Overflow/FluentOverflowItem.razor.cs | 4 +-- src/Core/Enums/OverflowItemFixed.cs | 31 +++++++++++++++++++ 6 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 src/Core/Enums/OverflowItemFixed.cs diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml index 35a63a5652..ca78959a7d 100644 --- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml +++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml @@ -7022,7 +7022,7 @@ - Gets or sets if this item does not participate in overflow logic. + Gets or sets whether this element does not participate in overflow logic, and will always be visible. Defaults to false @@ -13734,6 +13734,26 @@ The component is oriented vertically. + + + Possibility for an element not to participate in the overflow logic and always remain displayed. + + + + + If the item is out of the display, it disappears. + + + + + The element is always visible + + + + + The element is always visible, but its width can be reduced to display "...". + + Sizes for presence badge diff --git a/examples/Demo/Shared/Pages/Overflow/Examples/OverflowDefaultExample.razor b/examples/Demo/Shared/Pages/Overflow/Examples/OverflowDefaultExample.razor index 98810144bf..77ca1d4f7d 100644 --- a/examples/Demo/Shared/Pages/Overflow/Examples/OverflowDefaultExample.razor +++ b/examples/Demo/Shared/Pages/Overflow/Examples/OverflowDefaultExample.razor @@ -16,6 +16,20 @@ Installation
+

+ In the following example, the first element will always be displayed (fixed), but an ellipse (...) + will be added when the container size is too small. + Note: the element must be able to display this ellipse, which is the case for text (like below) but not for the FluentBadge. +

+
+ + Aspire; + Blazor; + Microsoft; + Azure; + DevOps + +

With below example the VisibleOnLoad parameter is set to false. Make sure the screen dimension is small enough to show an overflow badge with count. @@ -39,3 +53,10 @@ Installation

+ +@code +{ + static string[] Catalog = new[] { "Blazor", "WPF", "Microsoft", "#Framework", + "Electron", "WinForms", "MAUI", "Fluent", "Reality", + "Office", "Installation", "Azure", "DevOps" }; +} diff --git a/src/Core/Components/Overflow/FluentOverflow.razor.css b/src/Core/Components/Overflow/FluentOverflow.razor.css index e3a0dde5d1..99a3b596c9 100644 --- a/src/Core/Components/Overflow/FluentOverflow.razor.css +++ b/src/Core/Components/Overflow/FluentOverflow.razor.css @@ -1,4 +1,4 @@ -.fluent-overflow[orientation="horizontal"] { +.fluent-overflow[orientation="horizontal"] { display: flex; flex-direction: row; overflow-x: hidden; @@ -19,3 +19,10 @@ .fluent-overflow ::deep > *[overflow] { display: none; } + +.fluent-overflow ::deep .fluent-overflow-item[fixed='ellipsis'] { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: fit-content; +} diff --git a/src/Core/Components/Overflow/FluentOverflowItem.razor b/src/Core/Components/Overflow/FluentOverflowItem.razor index bc12287af7..deff9317a2 100644 --- a/src/Core/Components/Overflow/FluentOverflowItem.razor +++ b/src/Core/Components/Overflow/FluentOverflowItem.razor @@ -1,6 +1,6 @@ @namespace Microsoft.FluentUI.AspNetCore.Components @inherits FluentComponentBase -
+
@ChildContent
diff --git a/src/Core/Components/Overflow/FluentOverflowItem.razor.cs b/src/Core/Components/Overflow/FluentOverflowItem.razor.cs index 8dddd4208d..ac2a29ba09 100644 --- a/src/Core/Components/Overflow/FluentOverflowItem.razor.cs +++ b/src/Core/Components/Overflow/FluentOverflowItem.razor.cs @@ -35,11 +35,11 @@ public partial class FluentOverflowItem : IDisposable public RenderFragment? ChildContent { get; set; } /// - /// Gets or sets if this item does not participate in overflow logic. + /// Gets or sets whether this element does not participate in overflow logic, and will always be visible. /// Defaults to false /// [Parameter] - public bool Fixed { get; set; } = false; + public OverflowItemFixed Fixed { get; set; } = OverflowItemFixed.None; /// /// Gets True if this component is out of panel. diff --git a/src/Core/Enums/OverflowItemFixed.cs b/src/Core/Enums/OverflowItemFixed.cs new file mode 100644 index 0000000000..5b5b710d1e --- /dev/null +++ b/src/Core/Enums/OverflowItemFixed.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------------------ +// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------ + +using System.ComponentModel; + +namespace Microsoft.FluentUI.AspNetCore.Components; + +/// +/// Possibility for an element not to participate in the overflow logic and always remain displayed. +/// +public enum OverflowItemFixed +{ + /// + /// If the item is out of the display, it disappears. + /// + [Description("none")] + None = 0, + + /// + /// The element is always visible + /// + [Description("fixed")] + Fixed = 1, + + /// + /// The element is always visible, but its width can be reduced to display "...". + /// + [Description("ellipsis")] + Ellipsis = 2, +} From 3af0867f32062d5f149ca18bd8451a1d7959101c Mon Sep 17 00:00:00 2001 From: Denis Voituron Date: Fri, 19 Jul 2024 12:36:49 +0200 Subject: [PATCH 21/22] [TreeView] Fix the first item never selected (#2414) * Fix the first element not clickable * Remove extra spaces --- src/Core/Components/TreeView/FluentTreeItem.razor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Components/TreeView/FluentTreeItem.razor.cs b/src/Core/Components/TreeView/FluentTreeItem.razor.cs index 3c4007b626..6e1fedcd6a 100644 --- a/src/Core/Components/TreeView/FluentTreeItem.razor.cs +++ b/src/Core/Components/TreeView/FluentTreeItem.razor.cs @@ -215,6 +215,7 @@ internal static RenderFragment GetFluentTreeItem(FluentTreeView owner, ITreeView builder.AddAttribute(i++, "Disabled", item.Disabled); builder.AddAttribute(i++, "IconCollapsed", item.IconCollapsed); builder.AddAttribute(i++, "IconExpanded", item.IconExpanded); + builder.SetKey(item.Id); if (owner.ItemTemplate != null) { From a62d9ce40828f71f6e1a9a4c9bb2d80e3a6bec55 Mon Sep 17 00:00:00 2001 From: Vincent Baaij Date: Fri, 19 Jul 2024 12:53:16 +0200 Subject: [PATCH 22/22] [Search] Add AutoComplete parameter (#2397) * [Search] Add AutoComplete parameter * Process PR comments --- .../Microsoft.FluentUI.AspNetCore.Components.xml | 6 ++++++ .../Pages/Search/Examples/SearchDefault.razor | 4 ++-- examples/Demo/Shared/Pages/Search/SearchPage.razor | 8 ++++++-- src/Core/Components/Search/FluentSearch.razor.cs | 14 +++++++++++--- src/Core/Components/Search/FluentSearch.razor.js | 10 +++++++++- 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml index ca78959a7d..dac01d88a3 100644 --- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml +++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml @@ -7858,6 +7858,12 @@ Gets or sets the visual appearance. See + + + Specifies whether a form or an input field should have autocomplete "on" or "off" or another value. + An Id value must be set to use this property. + + Gets or sets the content to be rendered inside the component. diff --git a/examples/Demo/Shared/Pages/Search/Examples/SearchDefault.razor b/examples/Demo/Shared/Pages/Search/Examples/SearchDefault.razor index 29c1fb6061..09a9572105 100644 --- a/examples/Demo/Shared/Pages/Search/Examples/SearchDefault.razor +++ b/examples/Demo/Shared/Pages/Search/Examples/SearchDefault.razor @@ -2,8 +2,8 @@ Without a label:
- +
@code { string? value; -} \ No newline at end of file +} diff --git a/examples/Demo/Shared/Pages/Search/SearchPage.razor b/examples/Demo/Shared/Pages/Search/SearchPage.razor index b44a41451f..6dbf60814c 100644 --- a/examples/Demo/Shared/Pages/Search/SearchPage.razor +++ b/examples/Demo/Shared/Pages/Search/SearchPage.razor @@ -7,7 +7,7 @@

Search

- An implementation of a search component. + An implementation of a search component. The <FluentSearch> supports two visual appearances, outline and filled, with the control defaulting to the outline appearance.

@@ -17,7 +17,11 @@

Examples

- + + +

The search box with a label has autocomplete turned off explicitly by using the AutoComplete parameter

+
+
diff --git a/src/Core/Components/Search/FluentSearch.razor.cs b/src/Core/Components/Search/FluentSearch.razor.cs index a901901fc8..ff56fb5a6a 100644 --- a/src/Core/Components/Search/FluentSearch.razor.cs +++ b/src/Core/Components/Search/FluentSearch.razor.cs @@ -62,6 +62,12 @@ public partial class FluentSearch : FluentInputBase [Parameter] public FluentInputAppearance Appearance { get; set; } = FluentInputAppearance.Outline; + /// + /// Gets or sets whether a form or an input field should have autocomplete "on" or "off" or another value. + /// + [Parameter] + public string? AutoComplete { get; set; } + /// /// Gets or sets the content to be rendered inside the component. /// @@ -79,11 +85,13 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (firstRender) { - //if (!string.IsNullOrEmpty(Id)) - //{ Module ??= await JSRuntime.InvokeAsync("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration)); await Module.InvokeVoidAsync("addAriaHidden", Id); - //} + + if (AutoComplete != null) + { + await Module.InvokeVoidAsync("setControlAttribute", Id, "autocomplete", AutoComplete); + } } } diff --git a/src/Core/Components/Search/FluentSearch.razor.js b/src/Core/Components/Search/FluentSearch.razor.js index 922defb70f..f5bd0e6084 100644 --- a/src/Core/Components/Search/FluentSearch.razor.js +++ b/src/Core/Components/Search/FluentSearch.razor.js @@ -4,4 +4,12 @@ export function addAriaHidden(id) { if (!!fieldElement) { fieldElement?.setAttribute("aria-hidden", "true"); } -} \ No newline at end of file +} + +export function setControlAttribute(id, attrName, value) { + const fieldElement = document.querySelector("#" + id)?.shadowRoot?.querySelector("#control"); + + if (!!fieldElement) { + fieldElement?.setAttribute(attrName, value); + } +}