From 6fb3518200f922bf50279b8d3ca4705e598eb1e7 Mon Sep 17 00:00:00 2001 From: Denis VOITURON Date: Sat, 27 Jun 2026 11:39:37 +0200 Subject: [PATCH 1/4] Add DemoNewsBar component and implement notification height adjustments --- .../Layout/DemoMainLayout.razor | 4 ++- .../Layout/DemoNewsBar.razor | 25 +++++++++++++++++++ .../Layout/DemoNewsBar.razor.cs | 10 ++++++++ .../Demo/FluentUI.Demo.Client/wwwroot/app.css | 14 +++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor create mode 100644 examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs diff --git a/examples/Demo/FluentUI.Demo.Client/Layout/DemoMainLayout.razor b/examples/Demo/FluentUI.Demo.Client/Layout/DemoMainLayout.razor index 017e513c53..c3a962b353 100644 --- a/examples/Demo/FluentUI.Demo.Client/Layout/DemoMainLayout.razor +++ b/examples/Demo/FluentUI.Demo.Client/Layout/DemoMainLayout.razor @@ -2,7 +2,9 @@ @App.PageTitle("Demo") - + + + @* ------------------- *@ @* Header *@ diff --git a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor new file mode 100644 index 0000000000..27e8f2b0d2 --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor @@ -0,0 +1,25 @@ + + Hello World! This is a demo of the Fluent UI Blazor components. + Hello World! This is a demo of the Fluent UI Blazor components. + + + \ No newline at end of file diff --git a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs new file mode 100644 index 0000000000..d6146c0242 --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs @@ -0,0 +1,10 @@ +// ------------------------------------------------------------------------ +// This file is licensed to you under the MIT License. +// ------------------------------------------------------------------------ + +namespace FluentUI.Demo.Client.Layout; + +public partial class DemoNewsBar +{ + +} \ No newline at end of file diff --git a/examples/Demo/FluentUI.Demo.Client/wwwroot/app.css b/examples/Demo/FluentUI.Demo.Client/wwwroot/app.css index 9279bef649..b9a50b51a0 100644 --- a/examples/Demo/FluentUI.Demo.Client/wwwroot/app.css +++ b/examples/Demo/FluentUI.Demo.Client/wwwroot/app.css @@ -18,6 +18,20 @@ body[data-media="xs"] svg g[data-name="MS-symbol"] { display: none; } +/* Web Site Notification bar height variable */ +body { + --demo-notification-height: 0px; +} + +body:has(fluent-message-bar.demo-notification) { + --demo-notification-height: 60px; +} + +.demo-notification { + height: var(--demo-notification-height); + overflow-y: auto; +} + /* Blazor Styles */ #blazor-error-ui { From 32b240359a259c382ea45a562f568ba8155c4612 Mon Sep 17 00:00:00 2001 From: Denis VOITURON Date: Sat, 27 Jun 2026 12:11:06 +0200 Subject: [PATCH 2/4] Implement dynamic news notification in DemoNewsBar with dismiss functionality --- .../Layout/DemoNewsBar.razor | 37 ++++++--- .../Layout/DemoNewsBar.razor.cs | 77 +++++++++++++++++++ 2 files changed, 105 insertions(+), 9 deletions(-) diff --git a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor index 27e8f2b0d2..1ffbf72e91 100644 --- a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor +++ b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor @@ -1,13 +1,32 @@ - - Hello World! This is a demo of the Fluent UI Blazor components. - Hello World! This is a demo of the Fluent UI Blazor components. - +@if (Visible) +{ + + +
+ @if (!string.IsNullOrEmpty(NewsContent)) + { + @NewsTitle: + } + @NewsContent +
+ + +
+ +
+} \ No newline at end of file diff --git a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs index d6146c0242..6bd0dd6c61 100644 --- a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs +++ b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs @@ -2,9 +2,86 @@ // This file is licensed to you under the MIT License. // ------------------------------------------------------------------------ +using System.Security.Cryptography; +using System.Text; +using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components; +using Microsoft.JSInterop; + namespace FluentUI.Demo.Client.Layout; public partial class DemoNewsBar { + private const string LocalStorageKey = "demo-newsbar-sha"; + + private static readonly Uri NewsUri = new("https://raw.githubusercontent.com/microsoft/fluentui-blazor/refs/heads/dev-v5/LICENSE.TXT"); + + private string NewsTitle { get; set; } = "News"; + + private MessageBarIntent NewsIntent { get; set; } = MessageBarIntent.Info; + + private string? NewsContent { get; set; } + + private string? NewsSha { get; set; } + + private bool Visible { get; set; } + + [Inject] + public required HttpClient HttpClient { get; set; } + + [Inject] + public required IJSRuntime JSRuntime { get; set; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!firstRender) + { + return; + } + + // Read the news content. + NewsContent = await HttpClient.GetStringAsync(NewsUri); + + if (string.IsNullOrWhiteSpace(NewsContent)) + { + Visible = false; + return; + } + + // Compute a small SHA of this content. + NewsSha = ComputeSha(NewsContent); + + // If the stored SHA matches the current content, the user already + // read this message, so the message bar stays hidden. + var storedSha = await JSRuntime.InvokeAsync("localStorage.getItem", LocalStorageKey); + Visible = !string.Equals(storedSha, NewsSha, StringComparison.Ordinal); + + StateHasChanged(); + + // When the content is set and the message bar is visible, apply the + // notification style override defined in the razor script. + if (Visible) + { + await JSRuntime.InvokeVoidAsync("applyDemoNotificationStyle"); + } + } + + private async Task DismissClickAsync() + { + // Save the SHA of the read message into the user local storage. + if (NewsSha is not null) + { + await JSRuntime.InvokeVoidAsync("localStorage.setItem", LocalStorageKey, NewsSha); + } + + Visible = false; + } + + private static string ComputeSha(string content) + { + var hash = SHA256.HashData(Encoding.UTF8.GetBytes(content)); + // Keep a small SHA: the first 8 bytes are enough to detect content changes. + return Convert.ToHexString(hash, 0, 8).ToLowerInvariant(); + } } \ No newline at end of file From 855aae9a040b44b0e93545824ba2828cd2e352bf Mon Sep 17 00:00:00 2001 From: Denis VOITURON Date: Sat, 27 Jun 2026 12:35:51 +0200 Subject: [PATCH 3/4] Add news banner functionality to DemoNewsBar with content parsing and styling --- NEWS-BANNER.md | 6 ++ .../Layout/DemoNewsBar.razor | 4 +- .../Layout/DemoNewsBar.razor.cs | 98 ++++++++++++++++++- .../Demo/FluentUI.Demo.Client/wwwroot/app.css | 53 +++++----- 4 files changed, 128 insertions(+), 33 deletions(-) create mode 100644 NEWS-BANNER.md diff --git a/NEWS-BANNER.md b/NEWS-BANNER.md new file mode 100644 index 0000000000..ac77936a63 --- /dev/null +++ b/NEWS-BANNER.md @@ -0,0 +1,6 @@ +--- +Title: New release available +Intent: Success +--- +Version RC4 is now available with many new components. +Check out the GitHub changelog for all the details and breaking changes. \ No newline at end of file diff --git a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor index 1ffbf72e91..4466c7e60f 100644 --- a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor +++ b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor @@ -3,11 +3,11 @@ - +
@if (!string.IsNullOrEmpty(NewsContent)) { - @NewsTitle: + @NewsTitle:  } @NewsContent
diff --git a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs index 6bd0dd6c61..7c69f620f7 100644 --- a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs +++ b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs @@ -10,11 +10,30 @@ namespace FluentUI.Demo.Client.Layout; +/// +/// A component that displays a news message bar in the demo application. +/// public partial class DemoNewsBar { - private const string LocalStorageKey = "demo-newsbar-sha"; + /* + -------------------------------------------------------------------- + NEWS-BANNER.md + -------------------------------------------------------------------- + + --- + Title: New release available + Intent: Success + --- + Version RC4 is now available with many new components. + Check out the changelog for all the details and breaking changes. + */ + + /// + /// The URI of the news content to display in the message bar. + /// + private static readonly Uri NewsUri = new("https://raw.githubusercontent.com/microsoft/fluentui-blazor/refs/heads/dev-v5/NEWS-BANNER.md"); - private static readonly Uri NewsUri = new("https://raw.githubusercontent.com/microsoft/fluentui-blazor/refs/heads/dev-v5/LICENSE.TXT"); + private const string LocalStorageKey = "demo-newsbar-sha"; private string NewsTitle { get; set; } = "News"; @@ -40,7 +59,24 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } // Read the news content. - NewsContent = await HttpClient.GetStringAsync(NewsUri); + var raw = ""; + + try + { + raw = await HttpClient.GetStringAsync(NewsUri); + } + catch (HttpRequestException) + { + } + + if (string.IsNullOrWhiteSpace(raw)) + { + Visible = false; + return; + } + + // Extract the Title, Intent and content from the front matter. + ParseNewsBanner(raw); if (string.IsNullOrWhiteSpace(NewsContent)) { @@ -48,8 +84,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender) return; } - // Compute a small SHA of this content. - NewsSha = ComputeSha(NewsContent); + // Compute a small SHA of the whole file content. + NewsSha = ComputeSha(raw); // If the stored SHA matches the current content, the user already // read this message, so the message bar stays hidden. @@ -84,4 +120,56 @@ private static string ComputeSha(string content) // Keep a small SHA: the first 8 bytes are enough to detect content changes. return Convert.ToHexString(hash, 0, 8).ToLowerInvariant(); } + + /// + /// Parses the news banner content using the front matter format: + /// a metadata block delimited by "---" lines containing the Title and + /// Intent, followed by the content body. + /// + /// The raw file content to parse. + private void ParseNewsBanner(string raw) + { + var lines = raw.Replace("\r\n", "\n").Split('\n'); + var index = 0; + + // The front matter must start with a "---" delimiter line. + if (index < lines.Length && lines[index].Trim() == "---") + { + index++; + + // Read the metadata until the closing "---" delimiter line. + while (index < lines.Length && lines[index].Trim() != "---") + { + var line = lines[index]; + var separator = line.IndexOf(':'); + + if (separator > 0) + { + var key = line[..separator].Trim(); + var value = line[(separator + 1)..].Trim(); + + if (string.Equals(key, "Title", StringComparison.OrdinalIgnoreCase)) + { + NewsTitle = value; + } + else if (string.Equals(key, "Intent", StringComparison.OrdinalIgnoreCase) + && Enum.TryParse(value, ignoreCase: true, out var intent)) + { + NewsIntent = intent; + } + } + + index++; + } + + // Skip the closing "---" delimiter line. + if (index < lines.Length) + { + index++; + } + } + + // The remaining lines are the content body. + NewsContent = string.Join(Environment.NewLine, lines[index..]).Trim(); + } } \ No newline at end of file diff --git a/examples/Demo/FluentUI.Demo.Client/wwwroot/app.css b/examples/Demo/FluentUI.Demo.Client/wwwroot/app.css index b9a50b51a0..a30d3de7e9 100644 --- a/examples/Demo/FluentUI.Demo.Client/wwwroot/app.css +++ b/examples/Demo/FluentUI.Demo.Client/wwwroot/app.css @@ -20,15 +20,16 @@ body[data-media="xs"] svg g[data-name="MS-symbol"] { /* Web Site Notification bar height variable */ body { - --demo-notification-height: 0px; + --demo-notification-height: 0px; } body:has(fluent-message-bar.demo-notification) { - --demo-notification-height: 60px; + --demo-notification-height: 60px; } .demo-notification { height: var(--demo-notification-height); + font-size: var(--fontSizeBase300); overflow-y: auto; } @@ -48,12 +49,12 @@ body:has(fluent-message-bar.demo-notification) { z-index: 1000; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; +} .blazor-error-boundary { background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; @@ -61,9 +62,9 @@ body:has(fluent-message-bar.demo-notification) { color: white; } - .blazor-error-boundary::after { - content: "An error has occurred." - } +.blazor-error-boundary::after { + content: "An error has occurred." +} /* loading-progress */ @@ -75,19 +76,19 @@ body:has(fluent-message-bar.demo-notification) { margin: 20vh auto 1rem auto; } - .loading-progress circle { - fill: none; - stroke: #e0e0e0; - stroke-width: 0.6rem; - transform-origin: 50% 50%; - transform: rotate(-90deg); - } +.loading-progress circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 0.6rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); +} - .loading-progress circle:last-child { - stroke: #1b6ec2; - stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; - transition: stroke-dasharray 0.05s ease-in-out; - } +.loading-progress circle:last-child { + stroke: #1b6ec2; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; +} .loading-progress-text { position: absolute; @@ -96,6 +97,6 @@ body:has(fluent-message-bar.demo-notification) { inset: calc(20vh + 3.25rem) 0 auto 0.2rem; } - .loading-progress-text:after { - content: var(--blazor-load-percentage-text, "Loading"); - } +.loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); +} \ No newline at end of file From c23723e39a8507068a644498ea7686eaa2bd0c19 Mon Sep 17 00:00:00 2001 From: Denis VOITURON Date: Wed, 1 Jul 2026 11:27:21 +0200 Subject: [PATCH 4/4] Refactor DemoNewsBar: Update news version message formatting, rename local storage key, and enhance SHA computation --- .../FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs index 7c69f620f7..50f95ab8ab 100644 --- a/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs +++ b/examples/Demo/FluentUI.Demo.Client/Layout/DemoNewsBar.razor.cs @@ -24,7 +24,7 @@ public partial class DemoNewsBar Title: New release available Intent: Success --- - Version RC4 is now available with many new components. + Version RC4 is now available with many new components. Check out the changelog for all the details and breaking changes. */ @@ -33,7 +33,7 @@ public partial class DemoNewsBar /// private static readonly Uri NewsUri = new("https://raw.githubusercontent.com/microsoft/fluentui-blazor/refs/heads/dev-v5/NEWS-BANNER.md"); - private const string LocalStorageKey = "demo-newsbar-sha"; + private const string LocalStorageKey = "fluentui-demo-newsbar-sha"; private string NewsTitle { get; set; } = "News"; @@ -67,6 +67,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } catch (HttpRequestException) { + Console.WriteLine($"DemoNewsBar: Failed to read the news content from {NewsUri}"); } if (string.IsNullOrWhiteSpace(raw)) @@ -117,8 +118,8 @@ private static string ComputeSha(string content) { var hash = SHA256.HashData(Encoding.UTF8.GetBytes(content)); - // Keep a small SHA: the first 8 bytes are enough to detect content changes. - return Convert.ToHexString(hash, 0, 8).ToLowerInvariant(); + // Keep a small SHA: the first 16 bytes are enough to detect content changes. + return Convert.ToHexString(hash, 0, 16).ToLowerInvariant(); } ///