From b5a0349e6e660ddeb92bec9e4a2cd53665930b1e Mon Sep 17 00:00:00 2001 From: Vladimir Enchev Date: Sat, 11 Apr 2026 09:58:00 +0300 Subject: [PATCH] Add custom CssPath to RadzenTheme and its using ThemeService Based on #2523 by @trucidare Closes #2523 Closes #2354 --- Radzen.Blazor.Tests/ThemeTests.cs | 95 ++++++++++++++++++++++++++++++ Radzen.Blazor/RadzenTheme.razor.cs | 9 ++- Radzen.Blazor/ThemeService.cs | 20 ++++++- 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 Radzen.Blazor.Tests/ThemeTests.cs diff --git a/Radzen.Blazor.Tests/ThemeTests.cs b/Radzen.Blazor.Tests/ThemeTests.cs new file mode 100644 index 000000000..f2677c21b --- /dev/null +++ b/Radzen.Blazor.Tests/ThemeTests.cs @@ -0,0 +1,95 @@ +using Bunit; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Radzen.Blazor.Tests +{ + public class ThemeTests + { + [Fact] + public void Theme_Renders_Embedded_Default_CssPath() + { + const string path = "_content/Radzen.Blazor/css"; + + using var ctx = new TestContext(); + ctx.Services.AddScoped(); + + var component = ctx.RenderComponent(parameters => + { + parameters.Add(p => p.Theme, "material"); + }); + + Assert.Contains(path, component.Markup); + } + + [Fact] + public void Theme_Renders_Non_Embedded_Default_CssPath() + { + const string path = "\"css"; + + using var ctx = new TestContext(); + ctx.Services.AddScoped(); + + var component = ctx.RenderComponent(parameters => + { + parameters.Add(p => p.Theme, "awesome"); + }); + + Assert.Contains($"{path}/awesome-base.css", component.Markup); + } + + [Fact] + public void Theme_Renders_Non_Embedded_Custom_CssPath() + { + const string path = "_content/custom-assembly/css"; + + using var ctx = new TestContext(); + ctx.Services.AddScoped(); + + var component = ctx.RenderComponent(parameters => + { + parameters.Add(p => p.CssPath, path); + parameters.Add(p => p.Theme, "my-light"); + }); + + Assert.Contains(path, component.Markup); + } + + [Fact] + public void Theme_Renders_Embedded_CssPath_For_Embedded_Themes() + { + const string path = "_content/Radzen.Blazor/css"; + const string customPath = "_content/custom-assembly/css"; + + using var ctx = new TestContext(); + ctx.Services.AddScoped(); + + var component = ctx.RenderComponent(parameters => + { + parameters.Add(p => p.CssPath, customPath); + parameters.Add(p => p.Theme, "material"); + }); + + Assert.Contains(path, component.Markup); + Assert.DoesNotContain(customPath, component.Markup); + } + + [Fact] + public void Theme_WcagHref_Uses_Custom_CssPath() + { + const string path = "_content/custom-assembly/css"; + + using var ctx = new TestContext(); + ctx.Services.AddScoped(); + + var component = ctx.RenderComponent(parameters => + { + parameters.Add(p => p.CssPath, path); + parameters.Add(p => p.Theme, "my-light"); + parameters.Add(p => p.Wcag, true); + }); + + Assert.Contains($"{path}/my-light-wcag.css", component.Markup); + } + } +} diff --git a/Radzen.Blazor/RadzenTheme.razor.cs b/Radzen.Blazor/RadzenTheme.razor.cs index d48e104ef..4c1b720c4 100644 --- a/Radzen.Blazor/RadzenTheme.razor.cs +++ b/Radzen.Blazor/RadzenTheme.razor.cs @@ -20,11 +20,17 @@ namespace Radzen.Blazor public string? Theme { get; set; } /// - /// When set to true the icon font will be preloadd. + /// When set to true the icon font will be preloaded. /// [Parameter] public bool PreloadIconFont { get; set; } = true; + /// + /// Custom css path. If set, it overwrites the default path. + /// + [Parameter] + public string? CssPath { get; set; } + /// /// Enables WCAG contrast requirements. If set to true additional CSS file will be loaded. /// @@ -52,6 +58,7 @@ namespace Radzen.Blazor { persistentComponentState = ServiceProvider.GetService(); + ThemeService.SetCssPath(CssPath); theme = ThemeService.Theme ?? GetCurrentTheme(); wcag = ThemeService.Wcag ?? Wcag; diff --git a/Radzen.Blazor/ThemeService.cs b/Radzen.Blazor/ThemeService.cs index 79ac35973..f6ad72289 100644 --- a/Radzen.Blazor/ThemeService.cs +++ b/Radzen.Blazor/ThemeService.cs @@ -405,6 +405,11 @@ namespace Radzen /// public bool? RightToLeft { get; private set; } + /// + /// Custom css path. If set, it overwrites the default path. + /// + public string? CssPath { get; private set; } + /// /// Raised when the theme changes. /// @@ -473,7 +478,11 @@ namespace Radzen internal string WcagHref => $"{Path}/{Theme}-wcag.css?v={Version}"; - private string Path => Embedded ? $"_content/Radzen.Blazor/css" : "css"; + private string Path => Embedded + ? $"_content/Radzen.Blazor/css" + : !string.IsNullOrEmpty(CssPath) + ? CssPath + : "css"; internal bool Embedded => Theme switch { @@ -517,5 +526,14 @@ namespace Radzen TriggerChange = true }); } + + /// + /// Sets a specific css path to the theme service. + /// + /// New css path to look for themes. + public void SetCssPath(string? cssPath) + { + CssPath = cssPath; + } } } \ No newline at end of file