Add extensions for TagHelperOutput to copy and merge from TagBuilder.
- Added tests to validate functionality of all TagHelperOutputExtension methods. #1319
This commit is contained in:
parent
20cc294c70
commit
63f8f7de6d
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility related extensions for <see cref="TagHelperOutput"/>.
|
||||
/// </summary>
|
||||
public static class TagHelperOutputExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Copies a user-provided attribute from <paramref name="context"/>'s
|
||||
/// <see cref="TagHelperContext.AllAttributes"/> to <paramref name="tagHelperOutput"/>'s
|
||||
/// <see cref="TagHelperOutput.Attributes"/>.
|
||||
/// </summary>
|
||||
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
||||
/// <param name="attributeName">The name of the bound attribute.</param>
|
||||
/// <param name="context">The <see cref="TagHelperContext"/>.</param>
|
||||
/// <remarks>Only copies the attribute if <paramref name="tagHelperOutput"/>'s
|
||||
/// <see cref="TagHelperOutput.Attributes"/> does not contain an attribute with the given
|
||||
/// <paramref name="attributeName"/></remarks>
|
||||
public static void CopyHtmlAttribute(this TagHelperOutput tagHelperOutput,
|
||||
string attributeName,
|
||||
TagHelperContext context)
|
||||
{
|
||||
// We look for the original attribute so we can restore the exact attribute name the user typed.
|
||||
var entry = context.AllAttributes.First(attribute =>
|
||||
attribute.Key.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!tagHelperOutput.Attributes.ContainsKey(entry.Key))
|
||||
{
|
||||
tagHelperOutput.Attributes.Add(entry.Key, entry.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all attributes from <paramref name="tagHelperOutput"/>'s
|
||||
/// <see cref="TagHelperOutput.Attributes"/> that have the given <paramref name="prefix"/>.
|
||||
/// </summary>
|
||||
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
||||
/// <param name="prefix">A prefix to look for.</param>
|
||||
/// <returns><see cref="KeyValuePair{string, string}"/>s with <see cref="KeyValuePair{string, string}.Key"/>
|
||||
/// starting with the given <paramref name="prefix"/>.</returns>
|
||||
public static IEnumerable<KeyValuePair<string, string>> FindPrefixedAttributes(
|
||||
this TagHelperOutput tagHelperOutput, string prefix)
|
||||
{
|
||||
// TODO: We will not need this method once https://github.com/aspnet/Razor/issues/89 is completed.
|
||||
|
||||
// We're only interested in HTML attributes that have the desired prefix.
|
||||
var prefixedAttributes = tagHelperOutput.Attributes
|
||||
.Where(attribute => attribute.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
.ToArray();
|
||||
|
||||
return prefixedAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the given <paramref name="tagBuilder"/> into the <paramref name="tagHelperOutput"/>.
|
||||
/// </summary>
|
||||
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
||||
/// <param name="tagBuilder">The <see cref="TagBuilder"/> to merge.</param>
|
||||
/// <remarks><paramref name="tagHelperOutput"/>'s <see cref="TagHelperOutput.Content"/> has the given
|
||||
/// <paramref name="tagBuilder"/>s <see cref="TagBuilder.InnerHtml"/> appended to it. This is to ensure
|
||||
/// multiple <see cref="ITagHelper"/>s running on the same HTML tag don't overwrite each other; therefore,
|
||||
/// this method may not be appropriate for all <see cref="ITagHelper"/> scenarios.</remarks>
|
||||
public static void Merge(this TagHelperOutput tagHelperOutput, TagBuilder tagBuilder)
|
||||
{
|
||||
tagHelperOutput.TagName = tagBuilder.TagName;
|
||||
tagHelperOutput.Content += tagBuilder.InnerHtml;
|
||||
|
||||
MergeAttributes(tagHelperOutput, tagBuilder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the given <see cref="tagBuilder"/>'s <see cref="TagBuilder.Attributes"/> into the
|
||||
/// <paramref name="tagHelperOutput"/>.
|
||||
/// </summary>
|
||||
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
||||
/// <param name="tagBuilder">The <see cref="TagBuilder"/> to merge attributes from.</param>
|
||||
/// <remarks>Existing <see cref="TagHelperOutput.Attributes"/> on the given <paramref name="tagHelperOutput"/>
|
||||
/// are not overriden; "class" attributes are merged with spaces.</remarks>
|
||||
public static void MergeAttributes(this TagHelperOutput tagHelperOutput, TagBuilder tagBuilder)
|
||||
{
|
||||
foreach (var attribute in tagBuilder.Attributes)
|
||||
{
|
||||
if (!tagHelperOutput.Attributes.ContainsKey(attribute.Key))
|
||||
{
|
||||
tagHelperOutput.Attributes.Add(attribute.Key, attribute.Value);
|
||||
}
|
||||
else if (attribute.Key.Equals("class", StringComparison.Ordinal))
|
||||
{
|
||||
tagHelperOutput.Attributes["class"] += " " + attribute.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given <paramref name="attributes"/> from <paramref name="tagHelperOutput"/>'s
|
||||
/// <see cref="TagHelperOutput.Attributes"/>.
|
||||
/// </summary>
|
||||
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
||||
/// <param name="attributes">Attributes to remove.</param>
|
||||
public static void RemoveRange(
|
||||
this TagHelperOutput tagHelperOutput, IEnumerable<KeyValuePair<string, string>> attributes)
|
||||
{
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
tagHelperOutput.Attributes.Remove(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
public class TagHelperOutputExtensionsTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("hello", "world")]
|
||||
[InlineData("HeLlO", "wOrLd")]
|
||||
public void CopyHtmlAttribute_CopiesOriginalAttributes(string attributeName, string attributeValue)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: string.Empty);
|
||||
var tagHelperContext = new TagHelperContext(
|
||||
new Dictionary<string, object>(StringComparer.Ordinal)
|
||||
{
|
||||
{ attributeName, attributeValue }
|
||||
});
|
||||
var expectedAttribute = new KeyValuePair<string, string>(attributeName, attributeValue);
|
||||
|
||||
// Act
|
||||
tagHelperOutput.CopyHtmlAttribute("hello", tagHelperContext);
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes);
|
||||
Assert.Equal(expectedAttribute, attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CopyHtmlAttribute_DoesNotOverrideAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var attributeName = "hello";
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>()
|
||||
{
|
||||
{ attributeName, "world2" }
|
||||
},
|
||||
content: string.Empty);
|
||||
var expectedAttribute = new KeyValuePair<string, string>(attributeName, "world2");
|
||||
var tagHelperContext = new TagHelperContext(
|
||||
new Dictionary<string, object>(StringComparer.Ordinal)
|
||||
{
|
||||
{ attributeName, "world" }
|
||||
});
|
||||
|
||||
// Act
|
||||
tagHelperOutput.CopyHtmlAttribute(attributeName, tagHelperContext);
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes);
|
||||
Assert.Equal(expectedAttribute, attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveRange_RemovesProvidedAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>()
|
||||
{
|
||||
{ "route-Hello", "World" },
|
||||
{ "Route-I", "Am" }
|
||||
},
|
||||
content: string.Empty);
|
||||
var expectedAttribute = new KeyValuePair<string, string>("type", "btn");
|
||||
tagHelperOutput.Attributes.Add(expectedAttribute);
|
||||
var attributes = tagHelperOutput.FindPrefixedAttributes("route-");
|
||||
|
||||
// Act
|
||||
tagHelperOutput.RemoveRange(attributes);
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes);
|
||||
Assert.Equal(expectedAttribute, attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindPrefixedAttributes_ReturnsEmpty_AttributeListIfNoAttributesPrefixed()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>()
|
||||
{
|
||||
{ "routeHello", "World" },
|
||||
{ "Routee-I", "Am" }
|
||||
},
|
||||
content: string.Empty);
|
||||
|
||||
// Act
|
||||
var attributes = tagHelperOutput.FindPrefixedAttributes("route-");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(attributes);
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("routeHello"));
|
||||
Assert.Equal(attribute.Value, "World");
|
||||
attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("Routee-I"));
|
||||
Assert.Equal(attribute.Value, "Am");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeAttributes_DoesNotReplace_TagHelperOutputAttributeValues()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: string.Empty);
|
||||
var expectedAttribute = new KeyValuePair<string, string>("type", "btn");
|
||||
tagHelperOutput.Attributes.Add(expectedAttribute);
|
||||
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
tagBuilder.Attributes.Add("type", "hello");
|
||||
|
||||
// Act
|
||||
tagHelperOutput.MergeAttributes(tagBuilder);
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes);
|
||||
Assert.Equal(expectedAttribute, attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeAttributes_AppendsClass_TagHelperOutputAttributeValues()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: string.Empty);
|
||||
tagHelperOutput.Attributes.Add("class", "Hello");
|
||||
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
tagBuilder.Attributes.Add("class", "btn");
|
||||
|
||||
var expectedAttribute = new KeyValuePair<string, string>("class", "Hello btn");
|
||||
|
||||
// Act
|
||||
tagHelperOutput.MergeAttributes(tagBuilder);
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes);
|
||||
Assert.Equal(expectedAttribute, attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeAttributes_DoesNotEncode_TagHelperOutputAttributeValues()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: string.Empty);
|
||||
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
var expectedAttribute = new KeyValuePair<string, string>("visible", "val < 3");
|
||||
tagBuilder.Attributes.Add(expectedAttribute);
|
||||
|
||||
// Act
|
||||
tagHelperOutput.MergeAttributes(tagBuilder);
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes);
|
||||
Assert.Equal(expectedAttribute, attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeAttributes_CopiesMultiple_TagHelperOutputAttributeValues()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: string.Empty);
|
||||
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
var expectedAttribute1 = new KeyValuePair<string, string>("class", "btn");
|
||||
var expectedAttribute2 = new KeyValuePair<string, string>("class2", "btn");
|
||||
tagBuilder.Attributes.Add(expectedAttribute1);
|
||||
tagBuilder.Attributes.Add(expectedAttribute2);
|
||||
|
||||
// Act
|
||||
tagHelperOutput.MergeAttributes(tagBuilder);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, tagHelperOutput.Attributes.Count);
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("class"));
|
||||
Assert.Equal(expectedAttribute1.Value, attribute.Value);
|
||||
attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("class2"));
|
||||
Assert.Equal(expectedAttribute2.Value, attribute.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeAttributes_Maintains_TagHelperOutputAttributeValues()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: string.Empty);
|
||||
var expectedAttribute = new KeyValuePair<string, string>("class", "btn");
|
||||
tagHelperOutput.Attributes.Add(expectedAttribute);
|
||||
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
|
||||
// Act
|
||||
tagHelperOutput.MergeAttributes(tagBuilder);
|
||||
|
||||
// Assert
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes);
|
||||
Assert.Equal(expectedAttribute, attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeAttributes_Combines_TagHelperOutputAttributeValues()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: string.Empty);
|
||||
var expectedOutputAttribute = new KeyValuePair<string, string>("class", "btn");
|
||||
tagHelperOutput.Attributes.Add(expectedOutputAttribute);
|
||||
|
||||
var tagBuilder = new TagBuilder("p");
|
||||
var expectedBuilderAttribute = new KeyValuePair<string, string>("for", "hello");
|
||||
tagBuilder.Attributes.Add(expectedBuilderAttribute);
|
||||
|
||||
// Act
|
||||
tagHelperOutput.MergeAttributes(tagBuilder);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(tagHelperOutput.Attributes.Count, 2);
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("class"));
|
||||
Assert.Equal(expectedOutputAttribute.Value, attribute.Value);
|
||||
attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("for"));
|
||||
Assert.Equal(expectedBuilderAttribute.Value, attribute.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Merge_CombinesAllTagHelperOutputAndTagBuilderProperties()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperOutput = new TagHelperOutput(
|
||||
"p",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: "Hello from tagHelperOutput");
|
||||
var expectedOutputAttribute = new KeyValuePair<string, string>("class", "btn");
|
||||
tagHelperOutput.Attributes.Add(expectedOutputAttribute);
|
||||
|
||||
var tagBuilder = new TagBuilder("div");
|
||||
var expectedBuilderAttribute = new KeyValuePair<string, string>("for", "hello");
|
||||
tagBuilder.Attributes.Add(expectedBuilderAttribute);
|
||||
tagBuilder.InnerHtml = "Hello from tagBuilder.";
|
||||
|
||||
// Act
|
||||
tagHelperOutput.Merge(tagBuilder);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("div", tagHelperOutput.TagName);
|
||||
Assert.Equal("Hello from tagHelperOutputHello from tagBuilder.", tagHelperOutput.Content);
|
||||
Assert.Equal(tagHelperOutput.Attributes.Count, 2);
|
||||
var attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("class"));
|
||||
Assert.Equal(expectedOutputAttribute.Value, attribute.Value);
|
||||
attribute = Assert.Single(tagHelperOutput.Attributes, kvp => kvp.Key.Equals("for"));
|
||||
Assert.Equal(expectedBuilderAttribute.Value, attribute.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue