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