parent
9c07055ac7
commit
f8b0249918
|
|
@ -0,0 +1,57 @@
|
|||
// 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 Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ITagHelper"/> implementation targeting <label> elements with <c>for</c> attributes.
|
||||
/// </summary>
|
||||
[ContentBehavior(ContentBehavior.Modify)]
|
||||
public class LabelTagHelper : TagHelper
|
||||
{
|
||||
// Protected to ensure subclasses are correctly activated. Internal for ease of use when testing.
|
||||
[Activate]
|
||||
protected internal ViewContext ViewContext { get; set; }
|
||||
|
||||
// Protected to ensure subclasses are correctly activated. Internal for ease of use when testing.
|
||||
[Activate]
|
||||
protected internal IHtmlGenerator Generator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An expression to be evaluated against the current model.
|
||||
/// </summary>
|
||||
public ModelExpression For { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (For != null)
|
||||
{
|
||||
var tagBuilder = Generator.GenerateLabel(ViewContext,
|
||||
For.Metadata,
|
||||
For.Name,
|
||||
labelText: null,
|
||||
htmlAttributes: null);
|
||||
|
||||
if (tagBuilder != null)
|
||||
{
|
||||
output.MergeAttributes(tagBuilder);
|
||||
|
||||
// We check for whitespace to detect scenarios such as:
|
||||
// <label for="Name">
|
||||
// </label>
|
||||
if (string.IsNullOrWhiteSpace(output.Content))
|
||||
{
|
||||
output.Content = tagBuilder.InnerHtml;
|
||||
}
|
||||
|
||||
output.TagName = tagBuilder.TagName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Razor;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
public class LabelTagHelperTest
|
||||
{
|
||||
// Model (List<Model> or Model instance), container type (Model or NestModel), model accessor,
|
||||
// property path, TagHelperOutput.Content values.
|
||||
public static TheoryData<object, Type, Func<object>, string, TagHelperOutputContent> TestDataSet
|
||||
{
|
||||
get
|
||||
{
|
||||
var modelWithNull = new Model
|
||||
{
|
||||
NestedModel = new NestedModel
|
||||
{
|
||||
Text = null,
|
||||
},
|
||||
Text = null,
|
||||
};
|
||||
var modelWithText = new Model
|
||||
{
|
||||
NestedModel = new NestedModel
|
||||
{
|
||||
Text = "inner text",
|
||||
},
|
||||
Text = "outer text",
|
||||
};
|
||||
var models = new List<Model>
|
||||
{
|
||||
modelWithNull,
|
||||
modelWithText,
|
||||
};
|
||||
|
||||
return new TheoryData<object, Type, Func<object>, string, TagHelperOutputContent>
|
||||
{
|
||||
{ null, typeof(Model), () => null, "Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
|
||||
{ modelWithNull, typeof(Model), () => modelWithNull.Text, "Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
{ modelWithText, typeof(Model), () => modelWithText.Text, "Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
{ modelWithText, typeof(Model), () => modelWithNull.Text, "Text",
|
||||
new TagHelperOutputContent("Hello World", "Hello World") },
|
||||
{ modelWithText, typeof(Model), () => modelWithText.Text, "Text",
|
||||
new TagHelperOutputContent("Hello World", "Hello World") },
|
||||
|
||||
{ modelWithNull, typeof(NestedModel), () => modelWithNull.NestedModel.Text, "NestedModel.Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
{ modelWithText, typeof(NestedModel), () => modelWithText.NestedModel.Text, "NestedModel.Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
{ modelWithNull, typeof(NestedModel), () => modelWithNull.NestedModel.Text, "NestedModel.Text",
|
||||
new TagHelperOutputContent("Hello World", "Hello World") },
|
||||
{ modelWithText, typeof(NestedModel), () => modelWithText.NestedModel.Text, "NestedModel.Text",
|
||||
new TagHelperOutputContent("Hello World", "Hello World") },
|
||||
|
||||
// Note: Tests cases below here will not work in practice due to current limitations on indexing
|
||||
// into ModelExpressions. Will be fixed in https://github.com/aspnet/Mvc/issues/1345.
|
||||
{ models, typeof(Model), () => models[0].Text, "[0].Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
{ models, typeof(Model), () => models[1].Text, "[1].Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
{ models, typeof(Model), () => models[0].Text, "[0].Text",
|
||||
new TagHelperOutputContent("Hello World", "Hello World") },
|
||||
{ models, typeof(Model), () => models[1].Text, "[1].Text",
|
||||
new TagHelperOutputContent("Hello World", "Hello World") },
|
||||
|
||||
{ models, typeof(NestedModel), () => models[0].NestedModel.Text, "[0].NestedModel.Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
{ models, typeof(NestedModel), () => models[1].NestedModel.Text, "[1].NestedModel.Text",
|
||||
new TagHelperOutputContent(Environment.NewLine, "Text") },
|
||||
{ models, typeof(NestedModel), () => models[0].NestedModel.Text, "[0].NestedModel.Text",
|
||||
new TagHelperOutputContent("Hello World", "Hello World") },
|
||||
{ models, typeof(NestedModel), () => models[1].NestedModel.Text, "[1].NestedModel.Text",
|
||||
new TagHelperOutputContent("Hello World", "Hello World") },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestDataSet))]
|
||||
public async Task ProcessAsync_GeneratesExpectedOutput(
|
||||
object model,
|
||||
Type containerType,
|
||||
Func<object> modelAccessor,
|
||||
string propertyPath,
|
||||
TagHelperOutputContent tagHelperOutputContent)
|
||||
{
|
||||
// Arrange
|
||||
var expectedAttributes = new Dictionary<string, string>
|
||||
{
|
||||
{ "class", "form-control" },
|
||||
{ "for", propertyPath }
|
||||
};
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Property name is either nameof(Model.Text) or nameof(NestedModel.Text).
|
||||
var metadata = metadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName: "Text");
|
||||
var modelExpression = new ModelExpression(propertyPath, metadata);
|
||||
var tagHelper = new LabelTagHelper
|
||||
{
|
||||
For = modelExpression,
|
||||
};
|
||||
|
||||
var tagHelperContext = new TagHelperContext(allAttributes: new Dictionary<string, object>());
|
||||
var htmlAttributes = new Dictionary<string, string>
|
||||
{
|
||||
{ "class", "form-control" },
|
||||
};
|
||||
var output = new TagHelperOutput("A random tag name", htmlAttributes, tagHelperOutputContent.OriginalContent);
|
||||
var expectedTagName = "label";
|
||||
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||
tagHelper.ViewContext = viewContext;
|
||||
tagHelper.Generator = htmlGenerator;
|
||||
|
||||
// Act
|
||||
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedAttributes, output.Attributes);
|
||||
Assert.Equal(tagHelperOutputContent.ExpectedContent, output.Content);
|
||||
Assert.False(output.SelfClosing);
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TagHelper_LeavesOutputUnchanged_IfForNotBound2()
|
||||
{
|
||||
// Arrange
|
||||
var expectedAttributes = new Dictionary<string, string>
|
||||
{
|
||||
{ "class", "form-control" },
|
||||
};
|
||||
var expectedContent = "original content";
|
||||
var expectedTagName = "original tag name";
|
||||
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
modelAccessor: () => null,
|
||||
containerType: typeof(Model),
|
||||
propertyName: nameof(Model.Text));
|
||||
var modelExpression = new ModelExpression(nameof(Model.Text), metadata);
|
||||
var tagHelper = new LabelTagHelper();
|
||||
|
||||
var tagHelperContext = new TagHelperContext(allAttributes: new Dictionary<string, object>());
|
||||
var output = new TagHelperOutput(expectedTagName, expectedAttributes, expectedContent);
|
||||
|
||||
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||
Model model = null;
|
||||
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||
var activator = new DefaultTagHelperActivator();
|
||||
activator.Activate(tagHelper, viewContext);
|
||||
|
||||
// Act
|
||||
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedAttributes, output.Attributes);
|
||||
Assert.Equal(expectedContent, output.Content);
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
public class TagHelperOutputContent
|
||||
{
|
||||
public TagHelperOutputContent(string outputContent, string expectedContent)
|
||||
{
|
||||
OriginalContent = outputContent;
|
||||
ExpectedContent = expectedContent;
|
||||
}
|
||||
|
||||
public string OriginalContent { get; set; }
|
||||
public string ExpectedContent { get; set; }
|
||||
}
|
||||
|
||||
private class Model
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
public NestedModel NestedModel { get; set; }
|
||||
}
|
||||
|
||||
private class NestedModel
|
||||
{
|
||||
public string Text { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue