diff --git a/Razor.Blade/Blade/HtmlTagsService/HtmlTagsServiceImplementation_Manual.cs b/Razor.Blade/Blade/HtmlTagsService/HtmlTagsServiceImplementation_Manual.cs index 255eda43..0b719c5a 100644 --- a/Razor.Blade/Blade/HtmlTagsService/HtmlTagsServiceImplementation_Manual.cs +++ b/Razor.Blade/Blade/HtmlTagsService/HtmlTagsServiceImplementation_Manual.cs @@ -10,11 +10,11 @@ public partial class HtmlTagsServiceImplementation /// public Attribute Attr(string name, string value, AttributeOptions options = null) - => new Attribute(name, value, options); + => new Attribute(name, value, options ?? AttributeOptions.StandaloneOptions); /// public Attribute Attr(string name, object value = null, AttributeOptions options = null) - => new Attribute(name, value, options); + => new Attribute(name, value, options ?? AttributeOptions.StandaloneOptions); /// public TagCustom Custom(string name, params object[] content) diff --git a/Razor.Blade/Markup/Attribute.cs b/Razor.Blade/Markup/Attribute.cs index e4689ed9..7e121d51 100644 --- a/Razor.Blade/Markup/Attribute.cs +++ b/Razor.Blade/Markup/Attribute.cs @@ -55,7 +55,7 @@ private string Build() var currentOptions = AttributeOptions.UseOrCreate(Options); if (Value == null && currentOptions.DropValueIfNull) - return Name; + return currentOptions.AddPaddings ? $" {Name} " : Name; var val = Internals.Html.Encode(ValueStringOrSerialized(Value)) ?? ""; @@ -65,9 +65,11 @@ private string Build() val = val.Replace(Internals.Html.Encode(safeQuote), safeQuote); } - return currentOptions.KeepEmpty || !string.IsNullOrEmpty(val) - ? $"{Name}={currentOptions.Quote}{val}{currentOptions.Quote}" - : ""; + if (!currentOptions.KeepEmpty && string.IsNullOrEmpty(val)) + return ""; + + var result = $"{Name}={currentOptions.Quote}{val}{currentOptions.Quote}"; + return currentOptions.AddPaddings ? $" {result} " : result; } /// /// An sequence already prepared, so no more building would be necessary diff --git a/Razor.Blade/Markup/AttributeOptions.cs b/Razor.Blade/Markup/AttributeOptions.cs index c7ca3745..fa6a9e75 100644 --- a/Razor.Blade/Markup/AttributeOptions.cs +++ b/Razor.Blade/Markup/AttributeOptions.cs @@ -33,6 +33,8 @@ public class AttributeOptions /// public const bool DefaultDropValueIfNull = true; + public const bool DefaultAddSpacePaddings = false; + #endregion #region Constructor to enforce fluid API @@ -68,10 +70,15 @@ public AttributeOptions(AttributeOptions original = null, string quote = null, b /// public bool DropValueIfNull { get; } + public bool AddPaddings { get; private set; } + [PrivateApi] internal static AttributeOptions UseOrCreate(AttributeOptions original) => original ?? DefaultOptions; [PrivateApi] internal static AttributeOptions DefaultOptions = new AttributeOptions(); + + [PrivateApi] + internal static AttributeOptions StandaloneOptions = new AttributeOptions() { AddPaddings = true }; } } diff --git a/Razor.Blade/Razor.Blade.csproj b/Razor.Blade/Razor.Blade.csproj index f94a37ae..554c6db7 100644 --- a/Razor.Blade/Razor.Blade.csproj +++ b/Razor.Blade/Razor.Blade.csproj @@ -3,7 +3,7 @@ netstandard2.0;net472;net6.0;net7.0 ToSic.Razor - 04.04.00 + 04.04.01 ToSic.Razor ToSic.Razor ToSic.Razor diff --git a/ToSic.Razor.Dnn/BuildScripts/MSBuild.Community.Tasks.Targets b/ToSic.Razor.Dnn/BuildScripts/MSBuild.Community.Tasks.Targets index 2e165d5b..d682eb4a 100644 --- a/ToSic.Razor.Dnn/BuildScripts/MSBuild.Community.Tasks.Targets +++ b/ToSic.Razor.Dnn/BuildScripts/MSBuild.Community.Tasks.Targets @@ -3,7 +3,8 @@ $(MSBuildProjectDirectory)\BuildScripts $(MSBuildProjectDirectory)\..\..\bin - $(SolutionDir)\packages\MSBuildTasks.1.5.0.235\tools\MSBuild.Community.Tasks.dll + + diff --git a/ToSic.Razor.Dnn/Properties/AssemblyInfo.cs b/ToSic.Razor.Dnn/Properties/AssemblyInfo.cs index df3ab295..8e24b472 100644 --- a/ToSic.Razor.Dnn/Properties/AssemblyInfo.cs +++ b/ToSic.Razor.Dnn/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("04.04.00.00")] -[assembly: AssemblyFileVersion("04.04.00.00")] +[assembly: AssemblyVersion("04.04.01.00")] +[assembly: AssemblyFileVersion("04.04.01.00")] diff --git a/ToSic.Razor.Dnn/Razor.Blade.Dnn.csproj b/ToSic.Razor.Dnn/Razor.Blade.Dnn.csproj index ef30de43..28d54d9c 100644 --- a/ToSic.Razor.Dnn/Razor.Blade.Dnn.csproj +++ b/ToSic.Razor.Dnn/Razor.Blade.Dnn.csproj @@ -63,7 +63,7 @@ runtime - + runtime diff --git a/ToSic.Razor.Dnn/ToSic_Razor_Blade_Dnn.dnn b/ToSic.Razor.Dnn/ToSic_Razor_Blade_Dnn.dnn index a7b41750..9f7f242b 100644 --- a/ToSic.Razor.Dnn/ToSic_Razor_Blade_Dnn.dnn +++ b/ToSic.Razor.Dnn/ToSic_Razor_Blade_Dnn.dnn @@ -1,6 +1,6 @@ - + 2sic RazorBlade 2sic RazorBlade icon.png diff --git a/ToSic.RazorBladeTests/Blade/HtmlTagService/HtmlTagServiceImplementationTests.cs b/ToSic.RazorBladeTests/Blade/HtmlTagService/HtmlTagServiceImplementationTests.cs index 962476db..b32e4e98 100644 --- a/ToSic.RazorBladeTests/Blade/HtmlTagService/HtmlTagServiceImplementationTests.cs +++ b/ToSic.RazorBladeTests/Blade/HtmlTagService/HtmlTagServiceImplementationTests.cs @@ -14,5 +14,11 @@ public void H1Test() Is("

", HtmlTagsService.H1()); Is("

content

", HtmlTagsService.H1("content")); } + + [TestMethod()] + public void AttributesHaveSpaces() + { + Is(" name='value' ", HtmlTagsService.Attr("name", "value")); + } } } \ No newline at end of file diff --git a/ToSic.RazorBladeTests/TagBuilderTests/AttributeBuilderTests.cs b/ToSic.RazorBladeTests/TagBuilderTests/AttributeBuilderTests.cs index 87b1c1ab..a3b1c74c 100644 --- a/ToSic.RazorBladeTests/TagBuilderTests/AttributeBuilderTests.cs +++ b/ToSic.RazorBladeTests/TagBuilderTests/AttributeBuilderTests.cs @@ -6,11 +6,25 @@ namespace ToSic.RazorBladeTests.TagBuilderTests [TestClass] public class AttributeBuilderTests { + /// + /// Test accessor to reduce use-count + /// + public Attribute NewAttribute(string name, object? value = default, AttributeOptions? options = default) + => new Attribute(name, value, options); + [TestMethod] public void BasicAttributes() { - Assert.AreEqual("name='value'", new Attribute("name", "value").ToString()); - Assert.AreEqual("something='other'", new Attribute("something", "other").ToString()); + Assert.AreEqual("name='value'", NewAttribute("name", "value").ToString()); + Assert.AreEqual("something='other'", NewAttribute("something", "other").ToString()); + } + + [TestMethod] public void BasicAttributesStandalone() + { + var o = AttributeOptions.StandaloneOptions; + Assert.AreEqual(" name='value' ", NewAttribute("name", "value", o).ToString()); + Assert.AreEqual(" something='other' ", NewAttribute("something", "other", o).ToString()); + } [TestMethod] @@ -18,9 +32,9 @@ public void BasicAttributesQuote() { var options = new AttributeOptions(quote: "\""); Assert.AreEqual("name=\"value\"", - new Attribute("name", "value", options).ToString()); + NewAttribute("name", "value", options).ToString()); Assert.AreEqual("something=\"other\"", - new Attribute("something", "other", options).ToString()); + NewAttribute("something", "other", options).ToString()); } [TestMethod] @@ -28,18 +42,18 @@ public void UnEncodeQuote() { var options = new AttributeOptions(encodeQuotes: true); Assert.AreEqual("name='{\"name\":\"daniel\"}'", - new Attribute("name", "{\"name\":\"daniel\"}").ToString()); + NewAttribute("name", "{\"name\":\"daniel\"}").ToString()); Assert.AreEqual("name='{"name":"daniel"}'", - new Attribute("name", "{\"name\":\"daniel\"}", options).ToString()); + NewAttribute("name", "{\"name\":\"daniel\"}", options).ToString()); options = new AttributeOptions(quote: "\""); Assert.AreEqual("name=\"{"name":"daniel"}\"", - new Attribute("name", "{\"name\":\"daniel\"}", options).ToString(), + NewAttribute("name", "{\"name\":\"daniel\"}", options).ToString(), "with a different quote and encodeQuotes false"); options = new AttributeOptions(encodeQuotes: true, quote: "\""); Assert.AreEqual("name=\"{"name":"daniel"}\"", - new Attribute("name", "{\"name\":\"daniel\"}", options).ToString(), + NewAttribute("name", "{\"name\":\"daniel\"}", options).ToString(), "with a different quote and encodeQuotes = true"); } @@ -49,17 +63,17 @@ public void UnEncodeApostropheInValue() { var options = new AttributeOptions(quote: "\""); Assert.AreEqual("name=\"isn't it ironic\"", - new Attribute("name", "isn't it ironic", options).ToString(), + NewAttribute("name", "isn't it ironic", options).ToString(), "apostrophe with a different quote and encodeQuotes = false"); options = new AttributeOptions(encodeQuotes: true); Assert.AreEqual("name='isn't it ironic'", - new Attribute("name", "isn't it ironic", options).ToString(), + NewAttribute("name", "isn't it ironic", options).ToString(), "apostrophe with a different quote and encodeQuotes = true"); options = new AttributeOptions(encodeQuotes: true, quote: "\""); Assert.AreEqual("name=\"isn't it ironic\"", - new Attribute("name", "isn't it ironic", options).ToString(), + NewAttribute("name", "isn't it ironic", options).ToString(), "apostrophe with a different quote and encodeQuotes = true"); } @@ -68,16 +82,16 @@ public void UnEncodeApostropheInValue() public void BasicAttributesEmpty() { Assert.AreEqual("name=''", - new Attribute("name", "").ToString()); + NewAttribute("name", "").ToString()); Assert.AreEqual("name", - new Attribute("name", null).ToString()); + NewAttribute("name", null).ToString()); Assert.AreEqual("name=''", - new Attribute("name", null, + NewAttribute("name", null, new AttributeOptions(dropValueIfNull: false)).ToString()); var options = new AttributeOptions(keepEmpty: false); Assert.AreEqual("", - new Attribute("name", "", options).ToString()); + NewAttribute("name", "", options).ToString()); } [TestMethod] @@ -85,11 +99,11 @@ public void ObjectValues() { //var options = new AttributeOptions { KeepEmpty = false }; Assert.AreEqual("name='54'", - new Attribute("name", 54).ToString()); + NewAttribute("name", 54).ToString()); Assert.AreEqual("name='{\"Name\":\"Daniel\"}'", - new Attribute("name", new { Name = "Daniel" }).ToString()); + NewAttribute("name", new { Name = "Daniel" }).ToString()); Assert.AreEqual("name='Daniel'", - new Attribute("name", "Daniel" as object).ToString()); + NewAttribute("name", "Daniel" as object).ToString()); } } diff --git a/ToSic.RazorBladeTests/TagTests/TagAttributeTests.cs b/ToSic.RazorBladeTests/TagTests/TagAttributeTests.cs index c3f71262..6cb73f75 100644 --- a/ToSic.RazorBladeTests/TagTests/TagAttributeTests.cs +++ b/ToSic.RazorBladeTests/TagTests/TagAttributeTests.cs @@ -28,11 +28,18 @@ public void TagAttributeContainingPTag() } [TestMethod] - public void TagAttributeStandalone() + public void TagAttributeWithoutValue() { Is("
", TestDiv().Attr("data-fancybox").TagStart); } + [TestMethod] + public void TagAttributeOnly() + { + Is(" name='value' ", Tag.Attr("name", "value")); + } + + [TestMethod] public void TagAttributeMultiple() { diff --git a/docs/manifest.json b/docs/manifest.json index 891f67f3..fff58d9b 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1,6 +1,6 @@ { "homepages": [], - "source_base_path": "A:/razor-blade/DocFx Generator", + "source_base_path": "C:/Projects/razor-blades/DocFx Generator", "xrefmap": "xrefmap.yml", "files": [ { @@ -141,6 +141,9 @@ "version": "" }, { + "log_codes": [ + "UidNotFound" + ], "type": "Conceptual", "source_relative_path": "index.md", "output": { @@ -1777,11 +1780,11 @@ { "status": { "can_incremental": false, - "details": "Disable incremental build by force rebuild option.", + "details": "Cannot build incrementally because last build info is missing.", "incrementalPhase": "build", "total_file_count": 0, "skipped_file_count": 0, - "full_build_reason_code": "ForceRebuild" + "full_build_reason_code": "NoAvailableBuildCache" }, "processors": { "ConceptualDocumentProcessor": {