Add TagName attribute for tag helpers.
- Made it so the TagHelperDescriptorFactory could pull tag name targets from TagNameAttributes on types. - Tested the behavior between TagNameAttribute and TagHelperDescriptorFactory (the only consumer of the attribute). - Made sure to test duplicate and inherited class scenarios. #120
This commit is contained in:
parent
ec638b147a
commit
72c449bf86
|
|
@ -78,6 +78,22 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("ScopeManager_EndCannotBeCalledWithoutACallToBegin"), p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameter {0} must not contain null tag names.
|
||||
/// </summary>
|
||||
internal static string TagNameAttribute_AdditionalTagsCannotContainNull
|
||||
{
|
||||
get { return GetString("TagNameAttribute_AdditionalTagsCannotContainNull"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameter {0} must not contain null tag names.
|
||||
/// </summary>
|
||||
internal static string FormatTagNameAttribute_AdditionalTagsCannotContainNull(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagNameAttribute_AdditionalTagsCannotContainNull"), p0);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -131,4 +131,7 @@
|
|||
<data name="ScopeManager_EndCannotBeCalledWithoutACallToBegin" xml:space="preserve">
|
||||
<value>Must call '{2}.{1}' before calling '{2}.{0}'.</value>
|
||||
</data>
|
||||
<data name="TagNameAttribute_AdditionalTagsCannotContainNull" xml:space="preserve">
|
||||
<value>Parameter {0} must not contain null tag names.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -24,30 +24,40 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
/// <param name="type">The type to create a <see cref="TagHelperDescriptor"/> from.</param>
|
||||
/// <returns>A <see cref="TagHelperDescriptor"/> that describes the given <paramref name="type"/>.</returns>
|
||||
public static TagHelperDescriptor CreateDescriptor(Type type)
|
||||
public static IEnumerable<TagHelperDescriptor> CreateDescriptors(Type type)
|
||||
{
|
||||
var tagName = GetTagName(type);
|
||||
var tagNames = GetTagNames(type);
|
||||
var typeName = type.FullName;
|
||||
var attributeDescriptors = GetAttributeDescriptors(type);
|
||||
var contentBehavior = GetContentBehavior(type);
|
||||
|
||||
return new TagHelperDescriptor(tagName,
|
||||
typeName,
|
||||
contentBehavior,
|
||||
attributeDescriptors);
|
||||
return tagNames.Select(tagName =>
|
||||
new TagHelperDescriptor(tagName,
|
||||
typeName,
|
||||
contentBehavior,
|
||||
attributeDescriptors));
|
||||
}
|
||||
|
||||
// TODO: Make this method support TagNameAttribute tag names: https://github.com/aspnet/Razor/issues/120
|
||||
private static string GetTagName(Type tagHelperType)
|
||||
private static IEnumerable<string> GetTagNames(Type tagHelperType)
|
||||
{
|
||||
var name = tagHelperType.Name;
|
||||
var typeInfo = tagHelperType.GetTypeInfo();
|
||||
var attributes = typeInfo.GetCustomAttributes<TagNameAttribute>(inherit: false);
|
||||
|
||||
if (name.EndsWith(TagHelperNameEnding, StringComparison.OrdinalIgnoreCase))
|
||||
// If there isn't an attribute specifying the tag name derive it from the name
|
||||
if (!attributes.Any())
|
||||
{
|
||||
name = name.Substring(0, name.Length - TagHelperNameEnding.Length);
|
||||
var name = typeInfo.Name;
|
||||
|
||||
if (name.EndsWith(TagHelperNameEnding, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
name = name.Substring(0, name.Length - TagHelperNameEnding.Length);
|
||||
}
|
||||
|
||||
return new[] { name };
|
||||
}
|
||||
|
||||
return name;
|
||||
// Remove duplicate tag names.
|
||||
return attributes.SelectMany(attribute => attribute.Tags).Distinct();
|
||||
}
|
||||
|
||||
private static IEnumerable<TagHelperAttributeDescriptor> GetAttributeDescriptors(Type type)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
var tagHelperTypes = ResolveTagHelperTypes(lookupStrings);
|
||||
|
||||
// Convert types to TagHelperDescriptors
|
||||
var descriptors = tagHelperTypes.Select(TagHelperDescriptorFactory.CreateDescriptor);
|
||||
var descriptors = tagHelperTypes.SelectMany(TagHelperDescriptorFactory.CreateDescriptors);
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to override a <see cref="ITagHelper"/>'s default tag name target.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||
public sealed class TagNameAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="TagNameAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tag">The HTML tag name for the <see cref="TagHelper"/> to target.</param>
|
||||
public TagNameAttribute([NotNull] string tag)
|
||||
{
|
||||
Tags = new[] { tag };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="TagNameAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tag">The HTML tag name for the <see cref="TagHelper"/> to target.</param>
|
||||
/// <param name="additionalTags">Additional HTML tag names for the <see cref="TagHelper"/> to target.</param>
|
||||
public TagNameAttribute([NotNull] string tag, [NotNull] params string[] additionalTags)
|
||||
{
|
||||
if (additionalTags.Contains(null))
|
||||
{
|
||||
throw new ArgumentNullException(
|
||||
nameof(additionalTags),
|
||||
Resources.FormatTagNameAttribute_AdditionalTagsCannotContainNull(nameof(additionalTags)));
|
||||
};
|
||||
|
||||
var allTags = new List<string>(additionalTags);
|
||||
allTags.Add(tag);
|
||||
|
||||
Tags = allTags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IEnumerable{string}"/> of tag names for the <see cref="TagHelper"/> to target.
|
||||
/// </summary>
|
||||
public IEnumerable<string> Tags { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -15,9 +15,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
var expectedDescriptor = new TagHelperDescriptor("Object", "System.Object", ContentBehavior.None);
|
||||
|
||||
// Act
|
||||
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(object));
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(object));
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
|
|
@ -35,9 +36,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
});
|
||||
|
||||
// Act
|
||||
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(SingleAttributeTagHelper));
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(SingleAttributeTagHelper));
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
|
|
@ -56,9 +58,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
});
|
||||
|
||||
// Act
|
||||
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(MissingAccessorTagHelper));
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(MissingAccessorTagHelper));
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
|
|
@ -78,13 +81,13 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
});
|
||||
|
||||
// Act
|
||||
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(PrivateAccessorTagHelper));
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(PrivateAccessorTagHelper));
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptor_ResolvesCustomContentBehavior()
|
||||
{
|
||||
|
|
@ -95,9 +98,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
ContentBehavior.Append);
|
||||
|
||||
// Act
|
||||
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(CustomContentBehaviorTagHelper));
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(CustomContentBehaviorTagHelper));
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
|
|
@ -111,13 +115,114 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
ContentBehavior.None);
|
||||
|
||||
// Act
|
||||
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
||||
typeof(InheritedCustomContentBehaviorTagHelper));
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptor_ResolvesMultipleTagHelperDescriptorsFromSingleType()
|
||||
{
|
||||
// Arrange
|
||||
var validProp = typeof(MultiTagTagHelper).GetProperty(nameof(MultiTagTagHelper.ValidAttribute));
|
||||
var expectedDescriptors = new[] {
|
||||
new TagHelperDescriptor(
|
||||
"div",
|
||||
typeof(MultiTagTagHelper).FullName,
|
||||
ContentBehavior.None,
|
||||
new[] {
|
||||
new TagHelperAttributeDescriptor(nameof(MultiTagTagHelper.ValidAttribute), validProp)
|
||||
}),
|
||||
new TagHelperDescriptor(
|
||||
"p",
|
||||
typeof(MultiTagTagHelper).FullName,
|
||||
ContentBehavior.None,
|
||||
new[] {
|
||||
new TagHelperAttributeDescriptor(nameof(MultiTagTagHelper.ValidAttribute), validProp)
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(MultiTagTagHelper));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptor_DoesntResolveInheritedTagNames()
|
||||
{
|
||||
// Arrange
|
||||
var validProp = typeof(InheritedMultiTagTagHelper).GetProperty(nameof(InheritedMultiTagTagHelper.ValidAttribute));
|
||||
var expectedDescriptor = new TagHelperDescriptor(
|
||||
"InheritedMultiTag",
|
||||
typeof(InheritedMultiTagTagHelper).FullName,
|
||||
ContentBehavior.None,
|
||||
new[] {
|
||||
new TagHelperAttributeDescriptor(nameof(InheritedMultiTagTagHelper.ValidAttribute), validProp)
|
||||
});
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(InheritedMultiTagTagHelper));
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptor_IgnoresDuplicateTagNamesFromAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDescriptors = new[] {
|
||||
new TagHelperDescriptor("p", typeof(DuplicateTagNameTagHelper).FullName, ContentBehavior.None),
|
||||
new TagHelperDescriptor("div", typeof(DuplicateTagNameTagHelper).FullName, ContentBehavior.None)
|
||||
};
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(DuplicateTagNameTagHelper));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptor_OverridesTagNameFromAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDescriptors = new[] {
|
||||
new TagHelperDescriptor("data-condition",
|
||||
typeof(OverrideNameTagHelper).FullName,
|
||||
ContentBehavior.None),
|
||||
};
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(OverrideNameTagHelper));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptor_GetsTagNamesFromMultipleAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDescriptors = new[] {
|
||||
new TagHelperDescriptor("span", typeof(MultipleAttributeTagHelper).FullName, ContentBehavior.None),
|
||||
new TagHelperDescriptor("p", typeof(MultipleAttributeTagHelper).FullName, ContentBehavior.None),
|
||||
new TagHelperDescriptor("div", typeof(MultipleAttributeTagHelper).FullName, ContentBehavior.None)
|
||||
};
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(MultipleAttributeTagHelper));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
[ContentBehavior(ContentBehavior.Append)]
|
||||
private class CustomContentBehaviorTagHelper
|
||||
{
|
||||
|
|
@ -126,5 +231,31 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
private class InheritedCustomContentBehaviorTagHelper : CustomContentBehaviorTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TagName("p", "div")]
|
||||
private class MultiTagTagHelper
|
||||
{
|
||||
public string ValidAttribute { get; set; }
|
||||
}
|
||||
|
||||
private class InheritedMultiTagTagHelper : MultiTagTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TagName("p", "p", "div", "div")]
|
||||
private class DuplicateTagNameTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TagName("data-condition")]
|
||||
private class OverrideNameTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TagName("span")]
|
||||
[TagName("div", "p")]
|
||||
private class MultipleAttributeTagHelper
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue