From ec638b147aad1f4dace4d2cf13b7dca37217f232 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Mon, 29 Sep 2014 18:03:27 -0700 Subject: [PATCH] Add ContentBehaviorAttribute for TagHelpers. - Added detection of custom ContentBehaviors via the ContentBehaviorAttribute in the TagHelperDescriptorFactory. - Updated some comments in the ContentBehavior enum. - Add tests to validate custom content behavior resolution. #122 --- .../TagHelpers/ContentBehaviorAttribute.cs | 31 +++++++++++ .../TagHelpers/TagHelperDescriptorFactory.cs | 8 ++- .../project.json | 6 ++- .../TagHelpers/ContentBehavior.cs | 11 ++-- .../TagHelperDescriptorFactoryTest.cs | 51 +++++++++++++++++-- 5 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ContentBehaviorAttribute.cs diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ContentBehaviorAttribute.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ContentBehaviorAttribute.cs new file mode 100644 index 0000000000..845e608151 --- /dev/null +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ContentBehaviorAttribute.cs @@ -0,0 +1,31 @@ +// 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 Microsoft.AspNet.Razor.TagHelpers; + +namespace Microsoft.AspNet.Razor.Runtime.TagHelpers +{ + /// + /// Used to override 's behavior when its + /// is invoked. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class ContentBehaviorAttribute : Attribute + { + /// + /// Instantiates a new instance of the class. + /// + /// The for the + /// . + public ContentBehaviorAttribute(ContentBehavior contentBehavior) + { + ContentBehavior = contentBehavior; + } + + /// + /// for the . + /// + public ContentBehavior ContentBehavior { get; private set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs index c7813265ca..fd1b02343c 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs @@ -66,10 +66,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return new TagHelperAttributeDescriptor(property.Name, property); } - // TODO: Make the content behavior pull from a ContentBehaviorAttribute: https://github.com/aspnet/Razor/issues/122 private static ContentBehavior GetContentBehavior(Type type) { - return ContentBehavior.None; + var typeInfo = type.GetTypeInfo(); + var contentBehaviorAttribute = typeInfo.GetCustomAttribute(inherit: false); + + return contentBehaviorAttribute != null ? + contentBehaviorAttribute.ContentBehavior : + ContentBehavior.None; } private static bool IsValidProperty(PropertyInfo property) diff --git a/src/Microsoft.AspNet.Razor.Runtime/project.json b/src/Microsoft.AspNet.Razor.Runtime/project.json index 58a82c4faf..052cfa9358 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/project.json +++ b/src/Microsoft.AspNet.Razor.Runtime/project.json @@ -5,6 +5,10 @@ }, "frameworks": { "net45": { }, - "aspnetcore50": { } + "aspnetcore50": { + "dependencies": { + "System.Reflection.Extensions": "4.0.0.0" + } + } } } diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs b/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs index c513d5a710..b98be35f07 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs @@ -9,15 +9,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers public enum ContentBehavior { /// - /// Indicates that the tag helper will not modify its inner HTML in any way. This is the default + /// Indicates that a tag helper will not modify its content in any way. This is the default /// . /// /// Children of the current tag helper will execute after the current tag helper. None, /// - /// Indicates that the tag helper wants anything within its tag builder's inner HTML to be - /// appended to content its children generate. + /// Indicates the tag helper's content should be appended to what its children generate. /// /// Children of the current tag helper will execute before the current tag helper. Append, @@ -30,15 +29,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers Modify, /// - /// Indicates that the tag helper wants anything within its tag builder's inner HTML to be - /// prepended to the content its children generate. + /// Indicates the tag helper's content should be prepended to what its children generate. /// /// Children of the current tag helper will execute after the current tag helper. Prepend, /// - /// Indicates that the tag helper wants anything within its tag builder's inner HTML to - /// replace any HTML inside of it. + /// Indicates the tag helper's content should replace the HTML its children generate. /// /// Children of the current tag helper will not execute. Replace, diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs index 1dc078faa7..6fd0813131 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public class TagHelperDescriptorFactoryTest { [Fact] - public void DescriptorFactory_BuildsDescriptorsFromSimpleTypes() + public void CreateDescriptor_BuildsDescriptorsFromSimpleTypes() { // Arrange var expectedDescriptor = new TagHelperDescriptor("Object", "System.Object", ContentBehavior.None); @@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } [Fact] - public void DescriptorFactory_BuildsDescriptorsWithConventionNames() + public void CreateDescriptor_BuildsDescriptorsWithConventionNames() { // Arrange var intProperty = typeof(SingleAttributeTagHelper).GetProperty(nameof(SingleAttributeTagHelper.IntAttribute)); @@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } [Fact] - public void DescriptorFactory_OnlyAcceptsPropertiesWithGetAndSet() + public void CreateDescriptor_OnlyAcceptsPropertiesWithGetAndSet() { // Arrange var validProperty = typeof(MissingAccessorTagHelper).GetProperty( @@ -63,7 +63,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } [Fact] - public void DescriptorFactory_OnlyAcceptsPropertiesWithPublicGetAndSet() + public void CreateDescriptor_OnlyAcceptsPropertiesWithPublicGetAndSet() { // Arrange var validProperty = typeof(PrivateAccessorTagHelper).GetProperty( @@ -83,5 +83,48 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Assert Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default); } + + + [Fact] + public void CreateDescriptor_ResolvesCustomContentBehavior() + { + // Arrange + var expectedDescriptor = new TagHelperDescriptor( + "CustomContentBehavior", + typeof(CustomContentBehaviorTagHelper).FullName, + ContentBehavior.Append); + + // Act + var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(CustomContentBehaviorTagHelper)); + + // Assert + Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default); + } + + [Fact] + public void CreateDescriptor_DoesNotResolveInheritedCustomContentBehavior() + { + // Arrange + var expectedDescriptor = new TagHelperDescriptor( + "InheritedCustomContentBehavior", + typeof(InheritedCustomContentBehaviorTagHelper).FullName, + ContentBehavior.None); + + // Act + var descriptor = TagHelperDescriptorFactory.CreateDescriptor( + typeof(InheritedCustomContentBehaviorTagHelper)); + + // Assert + Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default); + } + + [ContentBehavior(ContentBehavior.Append)] + private class CustomContentBehaviorTagHelper + { + } + + private class InheritedCustomContentBehaviorTagHelper : CustomContentBehaviorTagHelper + { + } } } \ No newline at end of file